Browse Source

Merge branch 'main' into main

pull/7566/head
kittygaming99 2 weeks ago
committed by GitHub
parent
commit
2e6ba23aa4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/typos.yml
  2. 2
      .github/workflows/zizmor.yml
  3. 2
      .pre-commit-config.yaml
  4. 372
      Cargo.lock
  5. 21
      Cargo.toml
  6. 3
      docker/DockerSettings.yaml
  7. 8
      docker/Dockerfile.alpine
  8. 2
      docker/Dockerfile.debian
  9. 10
      docker/render_template
  10. 2
      macros/Cargo.toml
  11. 2
      rust-toolchain.toml
  12. 2
      src/api/core/accounts.rs
  13. 7
      src/api/core/ciphers.rs
  14. 15
      src/api/identity.rs
  15. 9
      src/db/models/archive.rs
  16. 22
      src/db/models/attachment.rs
  17. 30
      src/db/models/auth_request.rs
  18. 22
      src/db/models/cipher.rs
  19. 92
      src/db/models/collection.rs
  20. 9
      src/db/models/device.rs
  21. 22
      src/db/models/emergency_access.rs
  22. 27
      src/db/models/event.rs
  23. 33
      src/db/models/folder.rs
  24. 127
      src/db/models/group.rs
  25. 68
      src/db/models/organization.rs
  26. 22
      src/db/models/send.rs
  27. 18
      src/db/models/user.rs
  28. 87
      src/http_client.rs
  29. 19
      src/storage.rs

2
.github/workflows/typos.yml

@ -23,4 +23,4 @@ jobs:
# When this version is updated, do not forget to update this in `.pre-commit-config.yaml` too # When this version is updated, do not forget to update this in `.pre-commit-config.yaml` too
- name: Spell Check Repo - name: Spell Check Repo
uses: crate-ci/typos@4d9c206a77c041268485162b8e2579ad7a5cb9a3 # v1.50.0 uses: crate-ci/typos@d43b6c087ac471e2ea7b8af622ff15f05c0c365b # v1.50.1

2
.github/workflows/zizmor.yml

@ -24,7 +24,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Run zizmor - name: Run zizmor
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3 uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
with: with:
# intentionally not scanning the entire repository, # intentionally not scanning the entire repository,
# since it contains integration tests. # since it contains integration tests.

2
.pre-commit-config.yaml

@ -18,7 +18,7 @@ repos:
# When this version is updated, do not forget to update this in `.github/workflows/typos.yaml` too # When this version is updated, do not forget to update this in `.github/workflows/typos.yaml` too
- repo: https://github.com/crate-ci/typos - repo: https://github.com/crate-ci/typos
rev: 4d9c206a77c041268485162b8e2579ad7a5cb9a3 # v1.50.0 rev: d43b6c087ac471e2ea7b8af622ff15f05c0c365b # v1.50.1
hooks: hooks:
- id: typos - id: typos
always_run: true always_run: true

372
Cargo.lock

@ -151,9 +151,9 @@ dependencies = [
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.43" version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" checksum = "24a8ec73eb862508b7041723c89386365894b4e7d9f6998bf1b8529e5b0ee254"
dependencies = [ dependencies = [
"compression-codecs", "compression-codecs",
"compression-core", "compression-core",
@ -318,7 +318,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -360,9 +360,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]] [[package]]
name = "aws-config" name = "aws-config"
version = "1.11.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a767267da9e2c2e189b2f9df8b5657e850ecf5352644734ba130d4a57095cf1b" checksum = "b8d7b388a9fc3a6db15a5ec778c38b354eff1364882c94d08e0252f7a47dcaa4"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-runtime", "aws-runtime",
@ -403,9 +403,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-runtime" name = "aws-runtime"
version = "1.9.1" version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9007227e10b5fed2f3e0a2beff489211e2b5604c400b7a9d5d81ca9d64c24bb" checksum = "ef47857a1d4488b528f4a5d5715fa7c3300820897824152234d3fa22b1426657"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-sigv4", "aws-sigv4",
@ -428,9 +428,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-sdk-sso" name = "aws-sdk-sso"
version = "1.108.0" version = "1.109.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c15301b04372832947916607983b114b3374b9db0be058a00fb7513800de1f05" checksum = "c3cfe74df5d9ad2fedd691973ad3521ebf4f27a3c68c792556686aedb5519bab"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"aws-credential-types", "aws-credential-types",
@ -454,9 +454,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-sdk-ssooidc" name = "aws-sdk-ssooidc"
version = "1.110.0" version = "1.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72cc2c205cb27108183cf1856333f7d584c2ba0f505421b4209ca5828f9ea899" checksum = "81b0ec31ed6191bd11350aae4b2004198f2db21350cb0a20c57e0a92e55dd161"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"aws-credential-types", "aws-credential-types",
@ -480,9 +480,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-sdk-sts" name = "aws-sdk-sts"
version = "1.113.0" version = "1.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68182ecb449f7537db0f4d5d25917789cf41e32074a9fe47b6a0b847fe1d2032" checksum = "ef45745026107ec30c4ef86bd8ae4b002e7e5f6a86e4225240bdf6b06a0b944a"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"aws-credential-types", "aws-credential-types",
@ -619,9 +619,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-runtime-api" name = "aws-smithy-runtime-api"
version = "1.15.0" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954c563ce84507722d2679f07a35d21b9c6466b3872d513020d0281fc8112ac9" checksum = "9c054752dd9e4dc73d0b75748c99ac2d0feafbf2f25c7b0516f03a3534161223"
dependencies = [ dependencies = [
"aws-smithy-async", "aws-smithy-async",
"aws-smithy-runtime-api-macros", "aws-smithy-runtime-api-macros",
@ -659,9 +659,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-smithy-types" name = "aws-smithy-types"
version = "1.6.2" version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce83ce9abbb198d25bc7131e468d0f9fe1257125e58c39f3f9fc9f5098c9647" checksum = "8f94d16e797ec62cd999fc9d5942b48fa7050c3093ddadff48e4d7528d16fcb9"
dependencies = [ dependencies = [
"base64-simd", "base64-simd",
"bytes", "bytes",
@ -694,9 +694,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-types" name = "aws-types"
version = "1.5.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eec1cd5469f328c782dc3e33d4153cf118a54e33cbb3356d60d16f89883e1f94" checksum = "209f3a6d82a6e9e5f94abbed94c7a26e1c052341002bf57a5fb5481f625896fc"
dependencies = [ dependencies = [
"aws-credential-types", "aws-credential-types",
"aws-smithy-async", "aws-smithy-async",
@ -895,9 +895,9 @@ dependencies = [
[[package]] [[package]]
name = "cached" name = "cached"
version = "3.1.1" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133b6b7d6a828c24d5055ef51e67457002b4017ae5ea3d1b1552ca35a44b1119" checksum = "c5a6cf8262820194a1488ece477f5fb5ba9256ef25229d525470e71d6e9a5835"
dependencies = [ dependencies = [
"ahash", "ahash",
"async-lock", "async-lock",
@ -911,9 +911,9 @@ dependencies = [
[[package]] [[package]]
name = "cached_proc_macro" name = "cached_proc_macro"
version = "3.0.0" version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da80977bd46ecf98c593b651e393260f852678283989b8b7c4079304fe5e8936" checksum = "ece0579b43cf6e927b3370c7d44359c7122e7e52b3ea635bc8484da571442edb"
dependencies = [ dependencies = [
"darling 0.20.11", "darling 0.20.11",
"proc-macro-crate", "proc-macro-crate",
@ -930,9 +930,9 @@ checksum = "f5813789573ae815c8b4be58c4428e0e7ae05f0227678ba9de332ded585b9159"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.4.4" version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@ -1011,9 +1011,9 @@ dependencies = [
[[package]] [[package]]
name = "compression-codecs" name = "compression-codecs"
version = "0.4.38" version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" checksum = "100590da849306918656ffbb22576bbdfc1382b50ad10d1d50ec177d3b205fb2"
dependencies = [ dependencies = [
"brotli", "brotli",
"compression-core", "compression-core",
@ -1025,9 +1025,9 @@ dependencies = [
[[package]] [[package]]
name = "compression-core" name = "compression-core"
version = "0.4.32" version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414"
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
@ -1134,6 +1134,12 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core_detect"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.17" version = "0.2.17"
@ -1191,27 +1197,27 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.16" version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.20" version = "0.9.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.22" version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
[[package]] [[package]]
name = "crunchy" name = "crunchy"
@ -1308,12 +1314,12 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core 0.24.1",
"darling_macro 0.23.0", "darling_macro 0.24.1",
] ]
[[package]] [[package]]
@ -1346,15 +1352,15 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.119", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -1381,13 +1387,13 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core 0.24.1",
"quote", "quote",
"syn 2.0.119", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -1570,9 +1576,9 @@ dependencies = [
[[package]] [[package]]
name = "diesel" name = "diesel"
version = "2.3.12" version = "2.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715377c6e464cb44bb89bd8487584240516c8d5052bc645d6babc50bb8be46c3" checksum = "e3b934ddbdcb2abb9f9fc9c30bd47bcc5618b615eea1d334cda5fdf8ff9b072a"
dependencies = [ dependencies = [
"bigdecimal", "bigdecimal",
"bitflags 2.13.1", "bitflags 2.13.1",
@ -1607,9 +1613,9 @@ dependencies = [
[[package]] [[package]]
name = "diesel_derives" name = "diesel_derives"
version = "2.3.9" version = "2.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1817b7f4279b947fc4cafddec12b0e5f8727141706561ce3ac94a60bddd1cf5" checksum = "ecbd51fb6c020672543641167efa4e6417ff7ad76849ed556ace3595e72de03a"
dependencies = [ dependencies = [
"diesel_table_macro_syntax", "diesel_table_macro_syntax",
"dsl_auto_type", "dsl_auto_type",
@ -1670,7 +1676,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -1809,11 +1815,17 @@ dependencies = [
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"core_detect",
"multiversion",
"multiversion_no_op",
"rustversion",
"scopeguard",
"simdutf8",
] ]
[[package]] [[package]]
@ -1908,9 +1920,9 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.11" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
[[package]] [[package]]
name = "flate2" name = "flate2"
@ -2028,7 +2040,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -2177,7 +2189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6" checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6"
dependencies = [ dependencies = [
"codemap", "codemap",
"indexmap 2.14.1", "indexmap 2.14.2",
"lasso", "lasso",
"once_cell", "once_cell",
"phf 0.11.3", "phf 0.11.3",
@ -2206,7 +2218,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.5.0", "http 1.5.0",
"indexmap 2.14.1", "indexmap 2.14.2",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@ -2299,9 +2311,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hickory-net" name = "hickory-net"
version = "0.26.1" version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" checksum = "084e7bd6a377435d568f652153e571b50970d7ccc1d1eeec0519f834632287e1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"cfg-if", "cfg-if",
@ -2323,9 +2335,9 @@ dependencies = [
[[package]] [[package]]
name = "hickory-proto" name = "hickory-proto"
version = "0.26.1" version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bab31817bfb44672a252e97fe81cd0c18d1b2cf892108922f6818820df8c643" checksum = "7e2da0694c15b44c6f68a6b05e0233617008c54080e31d6eb848d858a9c5b38d"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"idna", "idna",
@ -2343,9 +2355,9 @@ dependencies = [
[[package]] [[package]]
name = "hickory-resolver" name = "hickory-resolver"
version = "0.26.1" version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d58d28879ceecde6607729660c2667a081ccdc082e082675042793960f178c" checksum = "0e4f9f4603319422d482738f3f6fe5aac03157fdbfed1cd85a3ff45adb09072f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util", "futures-util",
@ -2483,9 +2495,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hybrid-array" name = "hybrid-array"
version = "0.4.14" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17"
dependencies = [ dependencies = [
"typenum", "typenum",
] ]
@ -2543,9 +2555,9 @@ dependencies = [
"http 1.5.0", "http 1.5.0",
"hyper 1.11.1", "hyper 1.11.1",
"hyper-util", "hyper-util",
"rustls 0.23.43", "rustls 0.23.44",
"tokio", "tokio",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.5",
"tower-service", "tower-service",
] ]
@ -2721,9 +2733,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.1" version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.17.1", "hashbrown 0.17.1",
@ -2752,9 +2764,9 @@ dependencies = [
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.12.1" version = "2.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -2918,9 +2930,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.104" version = "0.3.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util", "futures-util",
@ -2999,12 +3011,12 @@ dependencies = [
"nom 8.0.0", "nom 8.0.0",
"percent-encoding", "percent-encoding",
"quoted_printable", "quoted_printable",
"rustls 0.23.43", "rustls 0.23.44",
"rustls-native-certs", "rustls-native-certs",
"serde", "serde",
"socket2 0.6.5", "socket2 0.6.5",
"tokio", "tokio",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.5",
"tracing", "tracing",
"url", "url",
] ]
@ -3097,7 +3109,7 @@ name = "macros"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -3189,9 +3201,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.2.2" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi",
@ -3237,6 +3249,33 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "multiversion"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c"
dependencies = [
"multiversion-macros",
]
[[package]]
name = "multiversion-macros"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83"
dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 3.0.5",
]
[[package]]
name = "multiversion_no_op"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
[[package]] [[package]]
name = "mysqlclient-sys" name = "mysqlclient-sys"
version = "0.5.2" version = "0.5.2"
@ -3340,7 +3379,7 @@ checksum = "e4e98dc3b890f6c23a0f9d3d491a2823d0dea0fa656302a13dd225fa924112a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -3446,9 +3485,9 @@ dependencies = [
[[package]] [[package]]
name = "opendal" name = "opendal"
version = "0.58.2" version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33dbff14cc9bb085224256d6a81289d2f3202e85b06f408d42534b42162a4231" checksum = "f950151f9587a51a7bed70a15fa0cff464eae96e41ae7499f97067bdafdf43eb"
dependencies = [ dependencies = [
"opendal-core", "opendal-core",
"opendal-service-fs", "opendal-service-fs",
@ -3457,9 +3496,9 @@ dependencies = [
[[package]] [[package]]
name = "opendal-core" name = "opendal-core"
version = "0.58.2" version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48dbcef97d3eb7591db2c18d5cae95c836bcce07359b98d98dd6f4e861eb77b7" checksum = "a43405d217dfdfb543f58847336d3af672897dd1939bb7dcf314b63cf364f1c9"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"asyncband", "asyncband",
@ -3481,11 +3520,25 @@ dependencies = [
"web-time", "web-time",
] ]
[[package]]
name = "opendal-http-transport-reqwest"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401999057db611e592f883fcf2cbd6754ff37af587deaadd07b8c1398b2b6b06"
dependencies = [
"bytes",
"futures",
"http 1.5.0",
"http-body 1.1.0",
"opendal-core",
"reqwest",
]
[[package]] [[package]]
name = "opendal-service-fs" name = "opendal-service-fs"
version = "0.58.2" version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7ef1e1c45f3f89282a59073897e0d685e51385fed0aea771714789525cff996" checksum = "fb9caf04d6d38713299dd4abac984b95ab16ee20e1ff09160f495c5a64644083"
dependencies = [ dependencies = [
"bytes", "bytes",
"log", "log",
@ -3497,9 +3550,9 @@ dependencies = [
[[package]] [[package]]
name = "opendal-service-s3" name = "opendal-service-s3"
version = "0.58.2" version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64335f9f24ccb62ac36f1d976342b48611a75ba61979813a4f78a4ebd94de42" checksum = "388b1d39b62535c62803754ebef89808859558697366dbedd0299345887ba461"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"bytes", "bytes",
@ -3750,9 +3803,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "pest" name = "pest"
version = "2.9.0" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" checksum = "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad"
dependencies = [ dependencies = [
"memchr", "memchr",
"ucd-trie", "ucd-trie",
@ -3760,9 +3813,9 @@ dependencies = [
[[package]] [[package]]
name = "pest_derive" name = "pest_derive"
version = "2.9.0" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a83744a5c8455b8b3e0dc5031362780a347c878bdd11584d1a8984228cc88d" checksum = "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f"
dependencies = [ dependencies = [
"pest", "pest",
"pest_generator", "pest_generator",
@ -3770,9 +3823,9 @@ dependencies = [
[[package]] [[package]]
name = "pest_generator" name = "pest_generator"
version = "2.9.0" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd3451aa3de60d4b9a1e736885e4dea6b31617598026f12256ad566d63304a" checksum = "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5"
dependencies = [ dependencies = [
"pest", "pest",
"pest_meta", "pest_meta",
@ -3783,9 +3836,9 @@ dependencies = [
[[package]] [[package]]
name = "pest_meta" name = "pest_meta"
version = "2.9.0" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e04d3a0849e241d7dfce834c83b1c5edc8622009e8dd51a12ba1927c32f05496" checksum = "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e"
dependencies = [ dependencies = [
"pest", "pest",
] ]
@ -3939,9 +3992,9 @@ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
[[package]] [[package]]
name = "portable-atomic-util" name = "portable-atomic-util"
version = "0.2.7" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715"
dependencies = [ dependencies = [
"portable-atomic", "portable-atomic",
] ]
@ -4228,7 +4281,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -4348,11 +4401,11 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.13.4" version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.23.1",
"bytes", "bytes",
"cookie", "cookie",
"cookie_store", "cookie_store",
@ -4371,7 +4424,7 @@ dependencies = [
"mime", "mime",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls 0.23.43", "rustls 0.23.44",
"rustls-pki-types", "rustls-pki-types",
"rustls-platform-verifier", "rustls-platform-verifier",
"serde", "serde",
@ -4379,7 +4432,7 @@ dependencies = [
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.5",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@ -4453,7 +4506,7 @@ dependencies = [
"either", "either",
"figment", "figment",
"futures", "futures",
"indexmap 2.14.1", "indexmap 2.14.2",
"log", "log",
"memchr", "memchr",
"multer", "multer",
@ -4485,7 +4538,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46"
dependencies = [ dependencies = [
"devise", "devise",
"glob", "glob",
"indexmap 2.14.1", "indexmap 2.14.2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rocket_http", "rocket_http",
@ -4505,7 +4558,7 @@ dependencies = [
"futures", "futures",
"http 0.2.12", "http 0.2.12",
"hyper 0.14.32", "hyper 0.14.32",
"indexmap 2.14.1", "indexmap 2.14.2",
"log", "log",
"memchr", "memchr",
"pear", "pear",
@ -4640,9 +4693,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.43" version = "0.23.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba"
dependencies = [ dependencies = [
"log", "log",
"once_cell", "once_cell",
@ -4694,7 +4747,7 @@ dependencies = [
"jni", "jni",
"log", "log",
"once_cell", "once_cell",
"rustls 0.23.43", "rustls 0.23.44",
"rustls-native-certs", "rustls-native-certs",
"rustls-platform-verifier-android", "rustls-platform-verifier-android",
"rustls-webpki 0.103.15", "rustls-webpki 0.103.15",
@ -4906,7 +4959,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -4974,16 +5027,16 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.22.0" version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.23.1",
"bs58", "bs58",
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.14.1", "indexmap 2.14.2",
"jiff", "jiff",
"schemars 0.9.0", "schemars 0.9.0",
"schemars 1.2.2", "schemars 1.2.2",
@ -4995,14 +5048,14 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.22.0" version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc"
dependencies = [ dependencies = [
"darling 0.23.0", "darling 0.24.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -5142,9 +5195,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.2" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f"
[[package]] [[package]]
name = "socket2" name = "socket2"
@ -5271,9 +5324,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.4" version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -5389,7 +5442,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -5454,9 +5507,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.12.0" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@ -5492,7 +5545,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -5507,11 +5560,11 @@ dependencies = [
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.4" version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67"
dependencies = [ dependencies = [
"rustls 0.23.43", "rustls 0.23.44",
"tokio", "tokio",
] ]
@ -5611,7 +5664,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [ dependencies = [
"indexmap 2.14.1", "indexmap 2.14.2",
"serde", "serde",
"serde_spanned 0.6.9", "serde_spanned 0.6.9",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
@ -5625,7 +5678,7 @@ version = "0.25.13+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
dependencies = [ dependencies = [
"indexmap 2.14.1", "indexmap 2.14.2",
"toml_datetime 1.1.1+spec-1.1.0", "toml_datetime 1.1.1+spec-1.1.0",
"toml_parser", "toml_parser",
"winnow 1.0.4", "winnow 1.0.4",
@ -5901,9 +5954,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.13.2" version = "1.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "068e763e8279de7ab94b6afebded2cb701678af094feb1c12ccb061b4783c1be" checksum = "2799ffb329a792ecfd902b71306c8a815a6ef1c0470fa9953a6aa4d4cecbe511"
[[package]] [[package]]
name = "vaultwarden" name = "vaultwarden"
@ -5950,6 +6003,7 @@ dependencies = [
"num-derive", "num-derive",
"num-traits", "num-traits",
"opendal", "opendal",
"opendal-http-transport-reqwest",
"openidconnect", "openidconnect",
"openssl", "openssl",
"pastey 0.2.3", "pastey 0.2.3",
@ -5965,7 +6019,7 @@ dependencies = [
"rocket", "rocket",
"rocket_ws", "rocket_ws",
"rpassword", "rpassword",
"rustls 0.23.43", "rustls 0.23.44",
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
@ -6040,9 +6094,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.127" version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@ -6053,9 +6107,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.77" version = "0.4.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -6063,9 +6117,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.127" version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -6073,22 +6127,22 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.127" version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.5",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.127" version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -6108,9 +6162,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.104" version = "0.3.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -6583,18 +6637,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.56" version = "0.8.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.56" version = "0.8.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -6672,7 +6726,7 @@ checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.4", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -6689,27 +6743,27 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.3" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" checksum = "bf06bd8162af0734b344780deb55b42a2429ae430870d13fcc12f238e880fe6e"
dependencies = [ dependencies = [
"zstd-safe", "zstd-safe",
] ]
[[package]] [[package]]
name = "zstd-safe" name = "zstd-safe"
version = "7.2.4" version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" checksum = "ae42c0555055784c70058d19ba8e275528e8a99a706684868ace5da4e716a4ab"
dependencies = [ dependencies = [
"zstd-sys", "zstd-sys",
] ]
[[package]] [[package]]
name = "zstd-sys" name = "zstd-sys"
version = "2.0.16+zstd.1.5.7" version = "2.1.0+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0"
dependencies = [ dependencies = [
"cc", "cc",
"pkg-config", "pkg-config",

21
Cargo.toml

@ -12,7 +12,6 @@ members = ["macros"]
name = "vaultwarden" name = "vaultwarden"
version = "1.0.0" version = "1.0.0"
authors = ["Daniel García <dani-garcia@users.noreply.github.com>"] authors = ["Daniel García <dani-garcia@users.noreply.github.com>"]
readme = "README.md"
build = "build.rs" build = "build.rs"
repository.workspace = true repository.workspace = true
edition.workspace = true edition.workspace = true
@ -41,6 +40,7 @@ vendored_openssl = ["openssl/vendored"]
enable_mimalloc = ["dep:mimalloc"] enable_mimalloc = ["dep:mimalloc"]
s3 = [ s3 = [
"opendal/services-s3", "opendal/services-s3",
"dep:opendal-http-transport-reqwest",
"dep:aws-config", "dep:aws-config",
"dep:aws-credential-types", "dep:aws-credential-types",
"dep:aws-smithy-runtime-api", "dep:aws-smithy-runtime-api",
@ -107,7 +107,7 @@ serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151" serde_json = "1.0.151"
# A safe, extensible ORM and Query builder # A safe, extensible ORM and Query builder
diesel = { version = "2.3.12", features = ["chrono", "r2d2", "numeric"] } diesel = { version = "2.3.13", features = ["chrono", "r2d2", "numeric"] }
diesel_migrations = "2.3.2" diesel_migrations = "2.3.2"
derive_more = { version = "2.1.1", features = [ derive_more = { version = "2.1.1", features = [
@ -125,7 +125,7 @@ libsqlite3-sys = { version = "0.38.2", optional = true }
# Crypto-related libraries # Crypto-related libraries
rand = "0.10.2" rand = "0.10.2"
ring = "0.17.14" ring = "0.17.14"
rustls = { version = "0.23.43", features = ["ring", "std"], default-features = false } rustls = { version = "0.23.44", features = ["ring", "std"], default-features = false }
subtle = "2.6.1" subtle = "2.6.1"
# UUID generation # UUID generation
@ -183,7 +183,7 @@ email_address = "0.2.9"
handlebars = { version = "6.4.4", features = ["dir_source"] } handlebars = { version = "6.4.4", features = ["dir_source"] }
# HTTP client (Used for favicons, version check, DUO and HIBP API) # HTTP client (Used for favicons, version check, DUO and HIBP API)
reqwest = { version = "0.13.4", default-features = false, features = [ reqwest = { version = "0.13.5", default-features = false, features = [
# Misc # Misc
"charset", "charset",
"cookies", "cookies",
@ -201,7 +201,7 @@ reqwest = { version = "0.13.4", default-features = false, features = [
"socks", "socks",
"system-proxy", "system-proxy",
] } ] }
hickory-resolver = "0.26.1" hickory-resolver = "0.26.2"
# Favicon extraction libraries # Favicon extraction libraries
html5gum = "0.8.4" html5gum = "0.8.4"
@ -215,7 +215,7 @@ bytes = "1.12.1"
svg-hush = "0.9.7" svg-hush = "0.9.7"
# Cache function results (Used for version check and favicon fetching) # Cache function results (Used for version check and favicon fetching)
cached = { version = "3.1.1", features = ["async"] } cached = { version = "4.0.0", features = ["async"] }
# Used for custom short lived cookie jar during favicon extraction # Used for custom short lived cookie jar during favicon extraction
cookie = "0.18.2" cookie = "0.18.2"
@ -232,7 +232,7 @@ pastey = "0.2.3"
governor = "0.10.4" governor = "0.10.4"
# CIDR parsing for the trusted proxies of the client IP header # CIDR parsing for the trusted proxies of the client IP header
ipnet = "2.12.1" ipnet = "2.12.2"
# OIDC for SSO # OIDC for SSO
openidconnect = { version = "4.0.1", default-features = false } openidconnect = { version = "4.0.1", default-features = false }
@ -257,17 +257,18 @@ rpassword = "7.5.4"
grass_compiler = { version = "0.13.4", default-features = false } grass_compiler = { version = "0.13.4", default-features = false }
# File are accessed through Apache OpenDAL # File are accessed through Apache OpenDAL
opendal = { version = "0.58.2", default-features = false, features = ["services-fs"] } opendal = { version = "0.59.1", default-features = false, features = ["services-fs"] }
opendal-http-transport-reqwest = { version = "0.59.1", default-features = false, features = ["rustls-no-provider"], optional = true }
# For retrieving AWS credentials, including temporary SSO credentials # For retrieving AWS credentials, including temporary SSO credentials
aws-config = { version = "1.11.0", optional = true, default-features = false, features = [ aws-config = { version = "1.12.0", optional = true, default-features = false, features = [
"behavior-version-latest", "behavior-version-latest",
"credentials-process", "credentials-process",
"rt-tokio", "rt-tokio",
"sso", "sso",
] } ] }
aws-credential-types = { version = "1.3.0", optional = true } aws-credential-types = { version = "1.3.0", optional = true }
aws-smithy-runtime-api = { version = "1.15.0", optional = true } aws-smithy-runtime-api = { version = "1.16.0", optional = true }
http = { version = "1.5.0", optional = true } http = { version = "1.5.0", optional = true }
reqsign-aws-v4 = { version = "3.3.0", optional = true } reqsign-aws-v4 = { version = "3.3.0", optional = true }
reqsign-core = { version = "3.3.1", optional = true } reqsign-core = { version = "3.3.1", optional = true }

3
docker/DockerSettings.yaml

@ -5,7 +5,8 @@ vault_image_digest: "sha256:ba8bab66d4330ab9dbafa8f245bcbe99cf6ee3f2c8ce9b5fbb10
# We use the linux/amd64 platform shell scripts since there is no difference between the different platform scripts # We use the linux/amd64 platform shell scripts since there is no difference between the different platform scripts
# https://github.com/tonistiigi/xx | https://hub.docker.com/r/tonistiigi/xx/tags # https://github.com/tonistiigi/xx | https://hub.docker.com/r/tonistiigi/xx/tags
xx_image_digest: "sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707" xx_image_digest: "sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707"
rust_version: 1.98.0 # Rust version to be used # The `rust_version` variable is extracted from `rust-toolchain.toml`
# rust_version: x.yy.z # Rust version to be used
debian_version: trixie # Debian release name to be used debian_version: trixie # Debian release name to be used
alpine_version: "3.24" # Alpine version to be used alpine_version: "3.24" # Alpine version to be used
# For which platforms/architectures will we try to build images # For which platforms/architectures will we try to build images

8
docker/Dockerfile.alpine

@ -32,10 +32,10 @@ FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:ba8bab66d4330
########################## ALPINE BUILD IMAGES ########################## ########################## ALPINE BUILD IMAGES ##########################
## NOTE: The Alpine Base Images do not support other platforms then linux/amd64 and linux/arm64 ## NOTE: The Alpine Base Images do not support other platforms then linux/amd64 and linux/arm64
## And for Alpine we define all build images here, they will only be loaded when actually used ## And for Alpine we define all build images here, they will only be loaded when actually used
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.98.0 AS build_amd64 FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.98.1 AS build_amd64
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.98.0 AS build_arm64 FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.98.1 AS build_arm64
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.98.0 AS build_armv7 FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.98.1 AS build_armv7
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.98.0 AS build_armv6 FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.98.1 AS build_armv6
########################## BUILD IMAGE ########################## ########################## BUILD IMAGE ##########################
# hadolint ignore=DL3006 # hadolint ignore=DL3006

2
docker/Dockerfile.debian

@ -36,7 +36,7 @@ FROM --platform=linux/amd64 docker.io/tonistiigi/xx@sha256:c64defb9ed5a91eacb37f
########################## BUILD IMAGE ########################## ########################## BUILD IMAGE ##########################
# hadolint ignore=DL3006 # hadolint ignore=DL3006
FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.98.0-slim-trixie AS build FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.98.1-slim-trixie AS build
# hadolint ignore=DL3067 # hadolint ignore=DL3067
COPY --from=xx / / COPY --from=xx / /
ARG TARGETARCH ARG TARGETARCH

10
docker/render_template

@ -3,17 +3,23 @@
import os import os
import argparse import argparse
import json import json
import tomllib
import yaml import yaml
import jinja2 import jinja2
# Load settings file # Load settings file
with open("DockerSettings.yaml", 'r') as yaml_file: with open('DockerSettings.yaml', 'r', encoding='utf-8') as yaml_file:
yaml_data = yaml.safe_load(yaml_file) yaml_data = yaml.safe_load(yaml_file)
# Extract the rust_version from the rust-toolchain.toml file
script_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(script_dir, '..', 'rust-toolchain.toml'), 'rb') as toolchain_file:
yaml_data["rust_version"] = tomllib.load(toolchain_file)["toolchain"]["channel"]
settings_env = jinja2.Environment( settings_env = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.getcwd()), loader=jinja2.FileSystemLoader(os.getcwd()),
) )
settings_yaml = yaml.safe_load(settings_env.get_template("DockerSettings.yaml").render(yaml_data)) settings_yaml = yaml.safe_load(settings_env.get_template('DockerSettings.yaml').render(yaml_data))
args_parser = argparse.ArgumentParser() args_parser = argparse.ArgumentParser()
args_parser.add_argument('template_file', help='Jinja2 template file to render.') args_parser.add_argument('template_file', help='Jinja2 template file to render.')

2
macros/Cargo.toml

@ -14,7 +14,7 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0.47" quote = "1.0.47"
syn = "3.0.4" syn = "3.0.5"
[lints] [lints]
workspace = true workspace = true

2
rust-toolchain.toml

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "1.98.0" channel = "1.98.1"
components = [ "rustfmt", "clippy" ] components = [ "rustfmt", "clippy" ]
profile = "minimal" profile = "minimal"

2
src/api/core/accounts.rs

@ -1611,7 +1611,7 @@ async fn post_auth_request(
_ => err!("AuthRequest doesn't exist", "Device verification failed"), _ => err!("AuthRequest doesn't exist", "Device verification failed"),
}; };
let mut auth_request = AuthRequest::new( let auth_request = AuthRequest::new(
user.uuid.clone(), user.uuid.clone(),
data.device_identifier.clone(), data.device_identifier.clone(),
client_headers.device_type, client_headers.device_type,

7
src/api/core/ciphers.rs

@ -537,11 +537,12 @@ pub async fn update_cipher_from_data(
cipher.move_to_folder(data.folder_id, &headers.user.uuid, conn).await?; cipher.move_to_folder(data.folder_id, &headers.user.uuid, conn).await?;
cipher.set_favorite(data.favorite, &headers.user.uuid, conn).await?; cipher.set_favorite(data.favorite, &headers.user.uuid, conn).await?;
if let Some(dt_str) = data.archived_date { match data.archived_date {
match NaiveDateTime::parse_from_str(&dt_str, "%+") { Some(dt_str) => match NaiveDateTime::parse_from_str(&dt_str, "%+") {
Ok(dt) => cipher.set_archived_at(dt, &headers.user.uuid, conn).await?, Ok(dt) => cipher.set_archived_at(dt, &headers.user.uuid, conn).await?,
Err(err) => warn!("Error parsing ArchivedDate '{dt_str}': {err}"), Err(err) => warn!("Error parsing ArchivedDate '{dt_str}': {err}"),
} },
None => cipher.unarchive(&headers.user.uuid, conn).await?,
} }
if ut != UpdateType::None { if ut != UpdateType::None {

15
src/api/identity.rs

@ -3,7 +3,7 @@ use num_traits::FromPrimitive;
use rocket::{ use rocket::{
Route, Route,
form::{Form, FromForm}, form::{Form, FromForm},
http::{Cookie, CookieJar, SameSite}, http::{Accept, Cookie, CookieJar, MediaType, SameSite},
response::Redirect, response::Redirect,
serde::json::Json, serde::json::Json,
}; };
@ -1083,11 +1083,18 @@ enum RegisterVerificationResponse {
#[response(status = 204)] #[response(status = 204)]
NoContent(()), NoContent(()),
Token(Json<String>), Token(Json<String>),
PlainToken(String),
}
// Return JSON only when the client explicitly requests it, otherwise return plain text.
fn accepts_json(accept: Option<&Accept>) -> bool {
accept.is_some_and(|accept| accept.preferred().media_type() == &MediaType::JSON)
} }
#[post("/accounts/register/send-verification-email", data = "<data>")] #[post("/accounts/register/send-verification-email", data = "<data>")]
async fn register_verification_email( async fn register_verification_email(
data: Json<RegisterVerificationData>, data: Json<RegisterVerificationData>,
accept: Option<&Accept>,
ip: ClientIp, ip: ClientIp,
conn: DbConn, conn: DbConn,
) -> ApiResult<RegisterVerificationResponse> { ) -> ApiResult<RegisterVerificationResponse> {
@ -1125,7 +1132,11 @@ async fn register_verification_email(
} else { } else {
// If email verification is not required, return the token directly // If email verification is not required, return the token directly
// the clients will use this token to finish the registration // the clients will use this token to finish the registration
Ok(RegisterVerificationResponse::Token(Json(token))) Ok(if accepts_json(accept) {
RegisterVerificationResponse::Token(Json(token))
} else {
RegisterVerificationResponse::PlainToken(token)
})
} }
} }

9
src/db/models/archive.rs

@ -41,17 +41,20 @@ impl Archive {
) -> EmptyResult { ) -> EmptyResult {
User::update_uuid_revision(user_uuid, conn).await; User::update_uuid_revision(user_uuid, conn).await;
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
diesel::replace_into(archives::table) diesel::insert_into(archives::table)
.values(( .values((
archives::user_uuid.eq(user_uuid), archives::user_uuid.eq(user_uuid),
archives::cipher_uuid.eq(cipher_uuid), archives::cipher_uuid.eq(cipher_uuid),
archives::archived_at.eq(archived_at), archives::archived_at.eq(archived_at),
)) ))
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(archives::archived_at.eq(archived_at))
.execute(conn) .execute(conn)
.map_res("Error saving archive") .map_res("Error saving archive")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(archives::table) diesel::insert_into(archives::table)
.values(( .values((
archives::user_uuid.eq(user_uuid), archives::user_uuid.eq(user_uuid),

22
src/db/models/attachment.rs

@ -82,24 +82,16 @@ impl Attachment {
impl Attachment { impl Attachment {
pub async fn save(&self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(attachments::table) diesel::insert_into(attachments::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error saving attachment")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(attachments::table)
.filter(attachments::id.eq(&self.id))
.set(self)
.execute(conn)
.map_res("Error saving attachment")
}
Err(e) => Err(e.into()),
}.map_res("Error saving attachment")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(attachments::table) diesel::insert_into(attachments::table)
.values(self) .values(self)
.on_conflict(attachments::id) .on_conflict(attachments::id)

30
src/db/models/auth_request.rs

@ -82,31 +82,23 @@ impl AuthRequest {
} }
impl AuthRequest { impl AuthRequest {
pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(auth_requests::table) diesel::insert_into(auth_requests::table)
.values(&*self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error saving auth_request")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(auth_requests::table)
.filter(auth_requests::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error auth_request")
}
Err(e) => Err(e.into()),
}.map_res("Error auth_request")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(auth_requests::table) diesel::insert_into(auth_requests::table)
.values(&*self) .values(self)
.on_conflict(auth_requests::uuid) .on_conflict(auth_requests::uuid)
.do_update() .do_update()
.set(&*self) .set(self)
.execute(conn) .execute(conn)
.map_res("Error saving auth_request") .map_res("Error saving auth_request")
} }

22
src/db/models/cipher.rs

@ -440,24 +440,16 @@ impl Cipher {
self.updated_at = Utc::now().naive_utc(); self.updated_at = Utc::now().naive_utc();
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(ciphers::table) diesel::insert_into(ciphers::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn) .execute(conn)
{ .map_res("Error saving cipher")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(ciphers::table)
.filter(ciphers::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error saving cipher")
}
Err(e) => Err(e.into()),
}.map_res("Error saving cipher")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(ciphers::table) diesel::insert_into(ciphers::table)
.values(&*self) .values(&*self)
.on_conflict(ciphers::uuid) .on_conflict(ciphers::uuid)

92
src/db/models/collection.rs

@ -168,24 +168,16 @@ impl Collection {
self.update_users_revision(conn).await; self.update_users_revision(conn).await;
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(collections::table) diesel::insert_into(collections::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error saving collection")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(collections::table)
.filter(collections::uuid.eq(&self.uuid))
.set(self)
.execute(conn)
.map_res("Error saving collection")
}
Err(e) => Err(e.into()),
}.map_res("Error saving collection")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(collections::table) diesel::insert_into(collections::table)
.values(self) .values(self)
.on_conflict(collections::uuid) .on_conflict(collections::uuid)
@ -728,53 +720,30 @@ impl CollectionUser {
) -> EmptyResult { ) -> EmptyResult {
User::update_uuid_revision(user_uuid, conn).await; User::update_uuid_revision(user_uuid, conn).await;
let values = (
users_collections::user_uuid.eq(user_uuid),
users_collections::collection_uuid.eq(collection_uuid),
users_collections::read_only.eq(read_only),
users_collections::hide_passwords.eq(hide_passwords),
users_collections::manage.eq(manage),
);
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(users_collections::table) diesel::insert_into(users_collections::table)
.values(( .values(values)
users_collections::user_uuid.eq(user_uuid), .on_conflict(diesel::dsl::DuplicatedKeys)
users_collections::collection_uuid.eq(collection_uuid), .do_update()
users_collections::read_only.eq(read_only), .set(values)
users_collections::hide_passwords.eq(hide_passwords),
users_collections::manage.eq(manage),
))
.execute(conn) .execute(conn)
{ .map_res("Error adding user to collection")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users_collections::table)
.filter(users_collections::user_uuid.eq(user_uuid))
.filter(users_collections::collection_uuid.eq(collection_uuid))
.set((
users_collections::user_uuid.eq(user_uuid),
users_collections::collection_uuid.eq(collection_uuid),
users_collections::read_only.eq(read_only),
users_collections::hide_passwords.eq(hide_passwords),
users_collections::manage.eq(manage),
))
.execute(conn)
.map_res("Error adding user to collection")
}
Err(e) => Err(e.into()),
}.map_res("Error adding user to collection")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(users_collections::table) diesel::insert_into(users_collections::table)
.values(( .values(values)
users_collections::user_uuid.eq(user_uuid),
users_collections::collection_uuid.eq(collection_uuid),
users_collections::read_only.eq(read_only),
users_collections::hide_passwords.eq(hide_passwords),
users_collections::manage.eq(manage),
))
.on_conflict((users_collections::user_uuid, users_collections::collection_uuid)) .on_conflict((users_collections::user_uuid, users_collections::collection_uuid))
.do_update() .do_update()
.set(( .set(values)
users_collections::read_only.eq(read_only),
users_collections::hide_passwords.eq(hide_passwords),
users_collections::manage.eq(manage),
))
.execute(conn) .execute(conn)
.map_res("Error adding user to collection") .map_res("Error adding user to collection")
} }
@ -909,19 +878,18 @@ impl CollectionCipher {
Self::update_users_revision(collection_uuid, conn).await; Self::update_users_revision(collection_uuid, conn).await;
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
// Not checking for ForeignKey Constraints here. diesel::insert_into(ciphers_collections::table)
// Table ciphers_collections does not have ForeignKey Constraints which would cause conflicts.
// This table has no constraints pointing to itself, but only to others.
diesel::replace_into(ciphers_collections::table)
.values(( .values((
ciphers_collections::cipher_uuid.eq(cipher_uuid), ciphers_collections::cipher_uuid.eq(cipher_uuid),
ciphers_collections::collection_uuid.eq(collection_uuid), ciphers_collections::collection_uuid.eq(collection_uuid),
)) ))
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_nothing()
.execute(conn) .execute(conn)
.map_res("Error adding cipher to collection") .map_res("Error adding cipher to collection")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(ciphers_collections::table) diesel::insert_into(ciphers_collections::table)
.values(( .values((
ciphers_collections::cipher_uuid.eq(cipher_uuid), ciphers_collections::cipher_uuid.eq(cipher_uuid),

9
src/db/models/device.rs

@ -146,15 +146,18 @@ impl Device {
} }
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
crate::util::retry(|| crate::util::retry(||
diesel::replace_into(devices::table) diesel::insert_into(devices::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn), .execute(conn),
10, 10,
).map_res("Error saving device") ).map_res("Error saving device")
} }
postgresql { postgresql, sqlite {
crate::util::retry(|| crate::util::retry(||
diesel::insert_into(devices::table) diesel::insert_into(devices::table)
.values(&*self) .values(&*self)

22
src/db/models/emergency_access.rs

@ -146,24 +146,16 @@ impl EmergencyAccess {
self.updated_at = Utc::now().naive_utc(); self.updated_at = Utc::now().naive_utc();
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(emergency_access::table) diesel::insert_into(emergency_access::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn) .execute(conn)
{ .map_res("Error saving emergency access")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(emergency_access::table)
.filter(emergency_access::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error updating emergency access")
}
Err(e) => Err(e.into()),
}.map_res("Error saving emergency access")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(emergency_access::table) diesel::insert_into(emergency_access::table)
.values(&*self) .values(&*self)
.on_conflict(emergency_access::uuid) .on_conflict(emergency_access::uuid)

27
src/db/models/event.rs

@ -208,20 +208,23 @@ impl Event {
/// Basic Queries /// Basic Queries
pub async fn save(&self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
diesel::replace_into(event::table) diesel::insert_into(event::table)
.values(self) .values(self)
.execute(conn) .on_conflict(diesel::dsl::DuplicatedKeys)
.map_res("Error saving event") .do_update()
.set(self)
.execute(conn)
.map_res("Error saving event")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(event::table) diesel::insert_into(event::table)
.values(self) .values(self)
.on_conflict(event::uuid) .on_conflict(event::uuid)
.do_update() .do_update()
.set(self) .set(self)
.execute(conn) .execute(conn)
.map_res("Error saving event") .map_res("Error saving event")
} }
} }
} }

33
src/db/models/folder.rs

@ -77,24 +77,16 @@ impl Folder {
self.updated_at = Utc::now().naive_utc(); self.updated_at = Utc::now().naive_utc();
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(folders::table) diesel::insert_into(folders::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn) .execute(conn)
{ .map_res("Error saving folder")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(folders::table)
.filter(folders::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error saving folder")
}
Err(e) => Err(e.into()),
}.map_res("Error saving folder")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(folders::table) diesel::insert_into(folders::table)
.values(&*self) .values(&*self)
.on_conflict(folders::uuid) .on_conflict(folders::uuid)
@ -147,16 +139,15 @@ impl Folder {
impl FolderCipher { impl FolderCipher {
pub async fn save(&self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
// Not checking for ForeignKey Constraints here. diesel::insert_into(folders_ciphers::table)
// Table folders_ciphers does not have ForeignKey Constraints which would cause conflicts.
// This table has no constraints pointing to itself, but only to others.
diesel::replace_into(folders_ciphers::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_nothing()
.execute(conn) .execute(conn)
.map_res("Error adding cipher to folder") .map_res("Error adding cipher to folder")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(folders_ciphers::table) diesel::insert_into(folders_ciphers::table)
.values(self) .values(self)
.on_conflict((folders_ciphers::cipher_uuid, folders_ciphers::folder_uuid)) .on_conflict((folders_ciphers::cipher_uuid, folders_ciphers::folder_uuid))

127
src/db/models/group.rs

@ -166,24 +166,16 @@ impl Group {
self.revision_date = Utc::now().naive_utc(); self.revision_date = Utc::now().naive_utc();
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(groups::table) diesel::insert_into(groups::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn) .execute(conn)
{ .map_res("Error saving group")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(groups::table)
.filter(groups::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error saving group")
}
Err(e) => Err(e.into()),
}.map_res("Error saving group")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(groups::table) diesel::insert_into(groups::table)
.values(&*self) .values(&*self)
.on_conflict(groups::uuid) .on_conflict(groups::uuid)
@ -326,53 +318,30 @@ impl CollectionGroup {
group_user.update_user_revision(conn).await; group_user.update_user_revision(conn).await;
} }
let values = (
collections_groups::collections_uuid.eq(&self.collections_uuid),
collections_groups::groups_uuid.eq(&self.groups_uuid),
collections_groups::read_only.eq(&self.read_only),
collections_groups::hide_passwords.eq(&self.hide_passwords),
collections_groups::manage.eq(&self.manage),
);
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(collections_groups::table) diesel::insert_into(collections_groups::table)
.values(( .values(values)
collections_groups::collections_uuid.eq(&self.collections_uuid), .on_conflict(diesel::dsl::DuplicatedKeys)
collections_groups::groups_uuid.eq(&self.groups_uuid), .do_update()
collections_groups::read_only.eq(&self.read_only), .set(values)
collections_groups::hide_passwords.eq(&self.hide_passwords),
collections_groups::manage.eq(&self.manage),
))
.execute(conn) .execute(conn)
{ .map_res("Error adding group to collection")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(collections_groups::table)
.filter(collections_groups::collections_uuid.eq(&self.collections_uuid))
.filter(collections_groups::groups_uuid.eq(&self.groups_uuid))
.set((
collections_groups::collections_uuid.eq(&self.collections_uuid),
collections_groups::groups_uuid.eq(&self.groups_uuid),
collections_groups::read_only.eq(&self.read_only),
collections_groups::hide_passwords.eq(&self.hide_passwords),
collections_groups::manage.eq(&self.manage),
))
.execute(conn)
.map_res("Error adding group to collection")
}
Err(e) => Err(e.into()),
}.map_res("Error adding group to collection")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(collections_groups::table) diesel::insert_into(collections_groups::table)
.values(( .values(values)
collections_groups::collections_uuid.eq(&self.collections_uuid),
collections_groups::groups_uuid.eq(&self.groups_uuid),
collections_groups::read_only.eq(self.read_only),
collections_groups::hide_passwords.eq(self.hide_passwords),
collections_groups::manage.eq(self.manage),
))
.on_conflict((collections_groups::collections_uuid, collections_groups::groups_uuid)) .on_conflict((collections_groups::collections_uuid, collections_groups::groups_uuid))
.do_update() .do_update()
.set(( .set(values)
collections_groups::read_only.eq(self.read_only),
collections_groups::hide_passwords.eq(self.hide_passwords),
collections_groups::manage.eq(self.manage),
))
.execute(conn) .execute(conn)
.map_res("Error adding group to collection") .map_res("Error adding group to collection")
} }
@ -497,43 +466,25 @@ impl GroupUser {
pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
self.update_user_revision(conn).await; self.update_user_revision(conn).await;
let values = (
groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid),
groups_users::groups_uuid.eq(&self.groups_uuid),
);
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(groups_users::table) diesel::insert_into(groups_users::table)
.values(( .values(values)
groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid), .on_conflict(diesel::dsl::DuplicatedKeys)
groups_users::groups_uuid.eq(&self.groups_uuid), .do_nothing()
))
.execute(conn) .execute(conn)
{ .map_res("Error adding user to group")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(groups_users::table)
.filter(groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid))
.filter(groups_users::groups_uuid.eq(&self.groups_uuid))
.set((
groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid),
groups_users::groups_uuid.eq(&self.groups_uuid),
))
.execute(conn)
.map_res("Error adding user to group")
}
Err(e) => Err(e.into()),
}.map_res("Error adding user to group")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(groups_users::table) diesel::insert_into(groups_users::table)
.values(( .values(values)
groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid),
groups_users::groups_uuid.eq(&self.groups_uuid),
))
.on_conflict((groups_users::users_organizations_uuid, groups_users::groups_uuid)) .on_conflict((groups_users::users_organizations_uuid, groups_users::groups_uuid))
.do_update() .do_nothing()
.set((
groups_users::users_organizations_uuid.eq(&self.users_organizations_uuid),
groups_users::groups_uuid.eq(&self.groups_uuid),
))
.execute(conn) .execute(conn)
.map_res("Error adding user to group") .map_res("Error adding user to group")
} }

68
src/db/models/organization.rs

@ -353,25 +353,16 @@ impl Organization {
} }
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(organizations::table) diesel::insert_into(organizations::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error saving organization")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(organizations::table)
.filter(organizations::uuid.eq(&self.uuid))
.set(self)
.execute(conn)
.map_res("Error saving organization")
}
Err(e) => Err(e.into()),
}.map_res("Error saving organization")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(organizations::table) diesel::insert_into(organizations::table)
.values(self) .values(self)
.on_conflict(organizations::uuid) .on_conflict(organizations::uuid)
@ -753,24 +744,16 @@ impl Membership {
User::update_uuid_revision(&self.user_uuid, conn).await; User::update_uuid_revision(&self.user_uuid, conn).await;
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(users_organizations::table) diesel::insert_into(users_organizations::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error adding user to organization")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users_organizations::table)
.filter(users_organizations::uuid.eq(&self.uuid))
.set(self)
.execute(conn)
.map_res("Error adding user to organization")
},
Err(e) => Err(e.into()),
}.map_res("Error adding user to organization")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(users_organizations::table) diesel::insert_into(users_organizations::table)
.values(self) .values(self)
.on_conflict(users_organizations::uuid) .on_conflict(users_organizations::uuid)
@ -1186,25 +1169,16 @@ impl Membership {
impl OrganizationApiKey { impl OrganizationApiKey {
pub async fn save(&self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(organization_api_key::table) diesel::insert_into(organization_api_key::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(self)
.execute(conn) .execute(conn)
{ .map_res("Error saving organization")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(organization_api_key::table)
.filter(organization_api_key::uuid.eq(&self.uuid))
.set(self)
.execute(conn)
.map_res("Error saving organization")
}
Err(e) => Err(e.into()),
}.map_res("Error saving organization")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(organization_api_key::table) diesel::insert_into(organization_api_key::table)
.values(self) .values(self)
.on_conflict((organization_api_key::uuid, organization_api_key::org_uuid)) .on_conflict((organization_api_key::uuid, organization_api_key::org_uuid))

22
src/db/models/send.rs

@ -202,24 +202,16 @@ impl Send {
self.revision_date = Utc::now().naive_utc(); self.revision_date = Utc::now().naive_utc();
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
match diesel::replace_into(sends::table) diesel::insert_into(sends::table)
.values(&*self) .values(&*self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_update()
.set(&*self)
.execute(conn) .execute(conn)
{ .map_res("Error saving send")
Ok(_) => Ok(()),
// Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(sends::table)
.filter(sends::uuid.eq(&self.uuid))
.set(&*self)
.execute(conn)
.map_res("Error saving send")
}
Err(e) => Err(e.into()),
}.map_res("Error saving send")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(sends::table) diesel::insert_into(sends::table)
.values(&*self) .values(&*self)
.on_conflict(sends::uuid) .on_conflict(sends::uuid)

18
src/db/models/user.rs

@ -463,15 +463,17 @@ impl Invitation {
} }
db_run! { conn: db_run! { conn:
sqlite, mysql { // Not checking for ForeignKey Constraints here
// Not checking for ForeignKey Constraints here // Table invitations does not have any ForeignKey Constraints.
// Table invitations does not have any ForeignKey Constraints. mysql {
diesel::replace_into(invitations::table) diesel::insert_into(invitations::table)
.values(self) .values(self)
.on_conflict(diesel::dsl::DuplicatedKeys)
.do_nothing()
.execute(conn) .execute(conn)
.map_res("Error saving invitation") .map_res("Error saving invitation")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(invitations::table) diesel::insert_into(invitations::table)
.values(self) .values(self)
.on_conflict(invitations::email) .on_conflict(invitations::email)
@ -528,13 +530,13 @@ pub struct UserId(String);
impl SsoUser { impl SsoUser {
pub async fn save(&self, conn: &DbConn) -> EmptyResult { pub async fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn: db_run! { conn:
sqlite, mysql { mysql {
diesel::replace_into(sso_users::table) diesel::insert_into(sso_users::table)
.values(self) .values(self)
.execute(conn) .execute(conn)
.map_res("Error saving SSO user") .map_res("Error saving SSO user")
} }
postgresql { postgresql, sqlite {
diesel::insert_into(sso_users::table) diesel::insert_into(sso_users::table)
.values(self) .values(self)
.execute(conn) .execute(conn)

87
src/http_client.rs

@ -14,7 +14,10 @@ use reqwest::{
}; };
use url::Host; use url::Host;
use crate::{CONFIG, util::is_global}; use crate::{
CONFIG,
util::{get_env_bool, is_global},
};
pub fn make_http_request(method: reqwest::Method, url: &str) -> Result<reqwest::RequestBuilder, crate::Error> { pub fn make_http_request(method: reqwest::Method, url: &str) -> Result<reqwest::RequestBuilder, crate::Error> {
static INSTANCE: LazyLock<Client> = static INSTANCE: LazyLock<Client> =
@ -36,7 +39,7 @@ pub fn get_reqwest_client_builder(enforce_block: bool) -> ClientBuilder {
let mut headers = header::HeaderMap::new(); let mut headers = header::HeaderMap::new();
headers.insert(header::USER_AGENT, header::HeaderValue::from_static("Vaultwarden")); headers.insert(header::USER_AGENT, header::HeaderValue::from_static("Vaultwarden"));
let redirect_policy = reqwest::redirect::Policy::custom(|attempt| { let redirect_policy = reqwest::redirect::Policy::custom(move |attempt| {
if attempt.previous().len() >= 5 { if attempt.previous().len() >= 5 {
return attempt.error("Too many redirects"); return attempt.error("Too many redirects");
} }
@ -45,7 +48,7 @@ pub fn get_reqwest_client_builder(enforce_block: bool) -> ClientBuilder {
return attempt.error("Invalid host"); return attempt.error("Invalid host");
}; };
if let Err(e) = should_block_host(&host) { if enforce_block && let Err(e) = should_block_host(&host) {
return attempt.error(e); return attempt.error(e);
} }
@ -59,6 +62,14 @@ pub fn get_reqwest_client_builder(enforce_block: bool) -> ClientBuilder {
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))
} }
fn dns_prefer_ipv6() -> bool {
// CONFIG may require DNS to initialize, so avoid forcing it during bootstrap.
match LazyLock::get(&CONFIG) {
Some(config) => config.dns_prefer_ipv6(),
None => get_env_bool("DNS_PREFER_IPV6").unwrap_or(false),
}
}
fn should_block_ip(ip: IpAddr) -> bool { fn should_block_ip(ip: IpAddr) -> bool {
if !CONFIG.http_request_block_non_global_ips() { if !CONFIG.http_request_block_non_global_ips() {
return false; return false;
@ -258,12 +269,8 @@ impl CustomDnsResolver {
fn new() -> Arc<Self> { fn new() -> Arc<Self> {
TokioResolver::builder(TokioRuntimeProvider::default()) TokioResolver::builder(TokioRuntimeProvider::default())
.and_then(|mut builder| { .and_then(|mut builder| {
// Hickory's default since v0.26 is `Ipv6AndIpv4`, which sorts IPv6 first // Query both families; the preferred order is applied per lookup below.
// This might cause issues on IPv4 only systems or containers builder.options_mut().ip_strategy = hickory_resolver::config::LookupIpStrategy::Ipv4AndIpv6;
// Unless someone enabled DNS_PREFER_IPV6, use Ipv4AndIpv6, which returns IPv4 first which was our previous default
if !CONFIG.dns_prefer_ipv6() {
builder.options_mut().ip_strategy = hickory_resolver::config::LookupIpStrategy::Ipv4AndIpv6;
}
builder.build() builder.build()
}) })
.inspect_err(|e| warn!("Error creating Hickory resolver, falling back to default: {e:?}")) .inspect_err(|e| warn!("Error creating Hickory resolver, falling back to default: {e:?}"))
@ -289,6 +296,17 @@ impl CustomDnsResolver {
} }
} }
fn sort_addresses(addresses: &mut [SocketAddr], prefer_ipv6: bool) {
// `sort_by_key` orders `false` before `true`.
// When IPv6 is preferred, IPv6 addresses return `false` for `is_ipv4()` and sort first.
// When IPv4 is preferred, IPv4 addresses return `false` for `is_ipv6()` and sort first.
if prefer_ipv6 {
addresses.sort_by_key(SocketAddr::is_ipv4);
} else {
addresses.sort_by_key(SocketAddr::is_ipv6);
}
}
fn pre_resolve(name: &str, enforce_block: bool) -> Result<(), CustomHttpClientError> { fn pre_resolve(name: &str, enforce_block: bool) -> Result<(), CustomHttpClientError> {
let Ok(host) = get_valid_host(name) else { let Ok(host) = get_valid_host(name) else {
return Err(CustomHttpClientError::Invalid { return Err(CustomHttpClientError::Invalid {
@ -320,7 +338,9 @@ impl Resolve for CustomDns {
let this = Arc::clone(&self.resolver); let this = Arc::clone(&self.resolver);
Box::pin(async move { Box::pin(async move {
let name = name.as_str(); let name = name.as_str();
let results = this.resolve_domain(name, enforce_block).await?; let mut results = this.resolve_domain(name, enforce_block).await?;
// Recheck after bootstrap so long-lived clients adopt the loaded config.
sort_addresses(&mut results, dns_prefer_ipv6());
if results.is_empty() { if results.is_empty() {
warn!("Unable to resolve {name} to any valid IP address"); warn!("Unable to resolve {name} to any valid IP address");
} }
@ -339,10 +359,29 @@ pub(crate) mod aws {
}; };
use reqwest::Client; use reqwest::Client;
use super::get_reqwest_client_builder;
// Adapter that wraps reqwest to be compatible with the AWS SDK // Adapter that wraps reqwest to be compatible with the AWS SDK
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct AwsReqwestConnector { pub(crate) struct AwsReqwestConnector {
pub(crate) client: Client, client: Client,
}
impl AwsReqwestConnector {
pub(crate) fn new() -> Self {
let client = get_reqwest_client_builder(false).build().expect("Failed to build AWS HTTP client");
Self {
client,
}
}
}
fn connector_error(error: reqwest::Error) -> ConnectorError {
if error.is_timeout() {
ConnectorError::timeout(Box::new(error))
} else {
ConnectorError::io(Box::new(error))
}
} }
impl HttpConnector for AwsReqwestConnector { impl HttpConnector for AwsReqwestConnector {
@ -362,10 +401,10 @@ pub(crate) mod aws {
req_builder = req_builder.body(body_bytes.to_vec()); req_builder = req_builder.body(body_bytes.to_vec());
} }
let response = req_builder.send().await.map_err(|e| ConnectorError::io(Box::new(e)))?; let response = req_builder.send().await.map_err(connector_error)?;
let status = response.status().into(); let status = response.status().into();
let bytes = response.bytes().await.map_err(|e| ConnectorError::io(Box::new(e)))?; let bytes = response.bytes().await.map_err(connector_error)?;
Ok(HttpResponse::new(status, bytes.into())) Ok(HttpResponse::new(status, bytes.into()))
}; };
@ -391,7 +430,7 @@ pub(crate) mod aws {
mod tests { mod tests {
use super::*; use super::*;
use crate::util::is_global_hardcoded; use crate::util::is_global_hardcoded;
use std::net::Ipv4Addr; use std::net::{Ipv4Addr, Ipv6Addr};
use url::Host; use url::Host;
// === // ===
@ -404,6 +443,26 @@ mod tests {
} }
} }
#[test]
fn dns_setup_does_not_initialize_config() {
assert!(LazyLock::get(&CONFIG).is_none());
drop(CustomDns::instance(false));
assert!(LazyLock::get(&CONFIG).is_none());
}
#[test]
fn dns_preference_orders_addresses() {
let ipv4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
let ipv6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0);
let mut addresses = [ipv6, ipv4];
sort_addresses(&mut addresses, false);
assert_eq!(addresses, [ipv4, ipv6]);
sort_addresses(&mut addresses, true);
assert_eq!(addresses, [ipv6, ipv4]);
}
#[test] #[test]
fn dotted_decimal_loopback_normalizes() { fn dotted_decimal_loopback_normalizes() {
let ip = parse_to_ip("127.0.0.1").unwrap(); let ip = parse_to_ip("127.0.0.1").unwrap();

19
src/storage.rs

@ -77,10 +77,18 @@ pub(crate) fn operator_for_path(path: &str) -> Result<opendal::Operator, crate::
#[cfg(s3)] #[cfg(s3)]
mod s3 { mod s3 {
use std::sync::LazyLock;
use opendal_http_transport_reqwest::ReqwestTransport;
use reqwest::Url; use reqwest::Url;
use crate::error::Error; use crate::error::Error;
static HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
// Storage endpoints are administrator-configured and may be private.
crate::http_client::get_reqwest_client_builder(false).build().expect("Failed to build OpenDAL HTTP client")
});
pub(super) fn is_uri(path: &str) -> bool { pub(super) fn is_uri(path: &str) -> bool {
path.starts_with("s3://") path.starts_with("s3://")
} }
@ -177,12 +185,7 @@ mod s3 {
let chain = DEFAULT_CREDENTIAL_CHAIN let chain = DEFAULT_CREDENTIAL_CHAIN
.get_or_init(|| { .get_or_init(|| {
let reqwest_client = reqwest::Client::builder().build().unwrap(); let conf = ProviderConfig::default().with_http_client(AwsReqwestConnector::new());
let connector = AwsReqwestConnector {
client: reqwest_client,
};
let conf = ProviderConfig::default().with_http_client(connector);
DefaultCredentialsChain::builder().configure(conf).build() DefaultCredentialsChain::builder().configure(conf).build()
}) })
@ -236,7 +239,9 @@ mod s3 {
builder.credential_provider_chain(ProvideCredentialChain::new().push(OpenDALS3CredentialProvider)); builder.credential_provider_chain(ProvideCredentialChain::new().push(OpenDALS3CredentialProvider));
} }
Ok(opendal::Operator::new(builder)?) let http_transport = opendal::HttpTransporter::new(ReqwestTransport::new(HTTP_CLIENT.clone()));
let context = opendal::OperationContext::new().with_http_transport(http_transport);
Ok(opendal::Operator::new(builder)?.with_context(context))
} }
fn uri_has_option(uri: &opendal::OperatorUri, names: &[&str]) -> bool { fn uri_has_option(uri: &opendal::OperatorUri, names: &[&str]) -> bool {

Loading…
Cancel
Save