mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-07-29 23:05:06 +03:00
Compare commits
4
Commits
235cf88231
...
787822854c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
787822854c | ||
|
|
f62a7a66c8 | ||
|
|
3a1378f469 | ||
|
|
dde63e209e |
@@ -235,7 +235,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload artifacts to Github Actions and Attest the binaries
|
# Upload artifacts to Github Actions and Attest the binaries
|
||||||
- name: Attest binaries
|
- name: Attest binaries
|
||||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
with:
|
with:
|
||||||
subject-path: vaultwarden-${{ env.NORMALIZED_ARCH }}
|
subject-path: vaultwarden-${{ env.NORMALIZED_ARCH }}
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ jobs:
|
|||||||
# Attest container images
|
# Attest container images
|
||||||
- name: Attest - docker.io - ${{ matrix.base_image }}
|
- name: Attest - docker.io - ${{ matrix.base_image }}
|
||||||
if: ${{ vars.DOCKERHUB_REPO != '' && env.DIGEST_SHA != ''}}
|
if: ${{ vars.DOCKERHUB_REPO != '' && env.DIGEST_SHA != ''}}
|
||||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ vars.DOCKERHUB_REPO }}
|
subject-name: ${{ vars.DOCKERHUB_REPO }}
|
||||||
subject-digest: ${{ env.DIGEST_SHA }}
|
subject-digest: ${{ env.DIGEST_SHA }}
|
||||||
@@ -369,7 +369,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Attest - ghcr.io - ${{ matrix.base_image }}
|
- name: Attest - ghcr.io - ${{ matrix.base_image }}
|
||||||
if: ${{ vars.GHCR_REPO != '' && env.DIGEST_SHA != ''}}
|
if: ${{ vars.GHCR_REPO != '' && env.DIGEST_SHA != ''}}
|
||||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ vars.GHCR_REPO }}
|
subject-name: ${{ vars.GHCR_REPO }}
|
||||||
subject-digest: ${{ env.DIGEST_SHA }}
|
subject-digest: ${{ env.DIGEST_SHA }}
|
||||||
@@ -377,7 +377,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Attest - quay.io - ${{ matrix.base_image }}
|
- name: Attest - quay.io - ${{ matrix.base_image }}
|
||||||
if: ${{ vars.QUAY_REPO != '' && env.DIGEST_SHA != ''}}
|
if: ${{ vars.QUAY_REPO != '' && env.DIGEST_SHA != ''}}
|
||||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ vars.QUAY_REPO }}
|
subject-name: ${{ vars.QUAY_REPO }}
|
||||||
subject-digest: ${{ env.DIGEST_SHA }}
|
subject-digest: ${{ env.DIGEST_SHA }}
|
||||||
|
|||||||
@@ -50,6 +50,6 @@ jobs:
|
|||||||
severity: CRITICAL,HIGH
|
severity: CRITICAL,HIGH
|
||||||
|
|
||||||
- name: Upload Trivy scan results to GitHub Security tab
|
- name: Upload Trivy scan results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||||
with:
|
with:
|
||||||
sarif_file: 'trivy-results.sarif'
|
sarif_file: 'trivy-results.sarif'
|
||||||
|
|||||||
Generated
+47
-119
@@ -76,15 +76,6 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ar_archive_writer"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
|
|
||||||
dependencies = [
|
|
||||||
"object",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argon2"
|
name = "argon2"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -891,9 +882,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.57"
|
version = "1.2.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423"
|
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
@@ -948,16 +939,6 @@ dependencies = [
|
|||||||
"phf 0.12.1",
|
"phf 0.12.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chumsky"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown 0.14.5",
|
|
||||||
"stacker",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@@ -2639,14 +2620,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipconfig"
|
name = "ipconfig"
|
||||||
version = "0.3.2"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
|
checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"socket2 0.5.10",
|
"socket2 0.6.3",
|
||||||
"widestring",
|
"widestring",
|
||||||
"windows-sys 0.48.0",
|
"windows-registry",
|
||||||
"winreg",
|
"windows-result",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2761,10 +2743,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.91"
|
version = "0.3.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
@@ -2842,14 +2826,13 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lettre"
|
name = "lettre"
|
||||||
version = "0.11.19"
|
version = "0.11.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f"
|
checksum = "471816f3e24b85e820dee02cde962379ea1a669e5242f19c61bcbcffedf4c4fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chumsky",
|
|
||||||
"email-encoding",
|
"email-encoding",
|
||||||
"email_address",
|
"email_address",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
@@ -3049,9 +3032,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
@@ -3170,9 +3153,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
|
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
@@ -3269,15 +3252,6 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.37.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oid-registry"
|
name = "oid-registry"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -3857,16 +3831,6 @@ version = "2.0.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "psm"
|
|
||||||
version = "0.1.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
|
|
||||||
dependencies = [
|
|
||||||
"ar_archive_writer",
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "publicsuffix"
|
name = "publicsuffix"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@@ -4459,9 +4423,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.1"
|
version = "2.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
@@ -4815,9 +4779,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "1.0.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -4934,9 +4898,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.8"
|
version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simple_asn1"
|
name = "simple_asn1"
|
||||||
@@ -5040,19 +5004,6 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stacker"
|
|
||||||
version = "0.1.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"psm",
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "state"
|
name = "state"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -5399,7 +5350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_spanned 1.0.4",
|
"serde_spanned 1.1.0",
|
||||||
"toml_datetime 0.7.5+spec-1.1.0",
|
"toml_datetime 0.7.5+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow 0.7.15",
|
"winnow 0.7.15",
|
||||||
@@ -5439,9 +5390,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.10+spec-1.1.0"
|
version = "1.1.0+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 = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow 1.0.0",
|
"winnow 1.0.0",
|
||||||
]
|
]
|
||||||
@@ -5640,9 +5591,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
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 = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@@ -5689,9 +5640,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.22.0"
|
version = "1.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
|
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -5852,9 +5803,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.114"
|
version = "0.2.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -5865,23 +5816,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.64"
|
version = "0.4.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
|
checksum = "2d1faf851e778dfa54db7cd438b70758eba9755cb47403f3496edd7c8fc212f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"futures-util",
|
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"once_cell",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.114"
|
version = "0.2.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -5889,9 +5836,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.114"
|
version = "0.2.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -5902,9 +5849,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.114"
|
version = "0.2.115"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -5958,9 +5905,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.91"
|
version = "0.3.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
|
checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -6178,15 +6125,6 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -6433,16 +6371,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winreg"
|
|
||||||
version = "0.50.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
@@ -6616,18 +6544,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.47"
|
version = "0.8.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.47"
|
version = "0.8.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+2
-2
@@ -103,7 +103,7 @@ ring = "0.17.14"
|
|||||||
subtle = "2.6.1"
|
subtle = "2.6.1"
|
||||||
|
|
||||||
# UUID generation
|
# UUID generation
|
||||||
uuid = { version = "1.22.0", features = ["v4"] }
|
uuid = { version = "1.23.0", features = ["v4"] }
|
||||||
|
|
||||||
# Date and time libraries
|
# Date and time libraries
|
||||||
chrono = { version = "0.4.44", features = ["clock", "serde"], default-features = false }
|
chrono = { version = "0.4.44", features = ["clock", "serde"], default-features = false }
|
||||||
@@ -136,7 +136,7 @@ webauthn-rs-core = "0.5.4"
|
|||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
|
|
||||||
# Email libraries
|
# Email libraries
|
||||||
lettre = { version = "0.11.19", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
lettre = { version = "0.11.20", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
||||||
percent-encoding = "2.3.2" # URL encoding library used for URL's in the emails
|
percent-encoding = "2.3.2" # URL encoding library used for URL's in the emails
|
||||||
email_address = "0.2.9"
|
email_address = "0.2.9"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ vault_image_digest: "sha256:37c8661fa59dcdfbd3baa8366b6e950ef292b15adfeff1f57812
|
|||||||
# 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.94.0 # Rust version to be used
|
rust_version: 1.94.1 # 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.23" # Alpine version to be used
|
alpine_version: "3.23" # 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
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ FROM --platform=linux/amd64 docker.io/vaultwarden/web-vault@sha256:37c8661fa59dc
|
|||||||
########################## 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.94.0 AS build_amd64
|
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:x86_64-musl-stable-1.94.1 AS build_amd64
|
||||||
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.94.0 AS build_arm64
|
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:aarch64-musl-stable-1.94.1 AS build_arm64
|
||||||
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.94.0 AS build_armv7
|
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:armv7-musleabihf-stable-1.94.1 AS build_armv7
|
||||||
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.94.0 AS build_armv6
|
FROM --platform=$BUILDPLATFORM ghcr.io/blackdex/rust-musl:arm-musleabi-stable-1.94.1 AS build_armv6
|
||||||
|
|
||||||
########################## BUILD IMAGE ##########################
|
########################## BUILD IMAGE ##########################
|
||||||
# hadolint ignore=DL3006
|
# hadolint ignore=DL3006
|
||||||
|
|||||||
@@ -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.94.0-slim-trixie AS build
|
FROM --platform=$BUILDPLATFORM docker.io/library/rust:1.94.1-slim-trixie AS build
|
||||||
COPY --from=xx / /
|
COPY --from=xx / /
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.94.0"
|
channel = "1.94.1"
|
||||||
components = [ "rustfmt", "clippy" ]
|
components = [ "rustfmt", "clippy" ]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|||||||
+2
-2
@@ -472,7 +472,7 @@ async fn deauth_user(user_id: UserId, _token: AdminToken, conn: DbConn, nt: Noti
|
|||||||
}
|
}
|
||||||
|
|
||||||
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp(&conn).await?;
|
||||||
|
|
||||||
user.save(&conn).await
|
user.save(&conn).await
|
||||||
}
|
}
|
||||||
@@ -481,7 +481,7 @@ async fn deauth_user(user_id: UserId, _token: AdminToken, conn: DbConn, nt: Noti
|
|||||||
async fn disable_user(user_id: UserId, _token: AdminToken, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
async fn disable_user(user_id: UserId, _token: AdminToken, conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||||
let mut user = get_user_or_404(&user_id, &conn).await?;
|
let mut user = get_user_or_404(&user_id, &conn).await?;
|
||||||
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp(&conn).await?;
|
||||||
user.enabled = false;
|
user.enabled = false;
|
||||||
|
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|||||||
+22
-18
@@ -106,7 +106,6 @@ pub struct RegisterData {
|
|||||||
|
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
organization_user_id: Option<MembershipId>,
|
organization_user_id: Option<MembershipId>,
|
||||||
|
|
||||||
// Used only from the register/finish endpoint
|
// Used only from the register/finish endpoint
|
||||||
@@ -296,7 +295,7 @@ pub async fn _register(data: Json<RegisterData>, email_verification: bool, conn:
|
|||||||
|
|
||||||
set_kdf_data(&mut user, &data.kdf)?;
|
set_kdf_data(&mut user, &data.kdf)?;
|
||||||
|
|
||||||
user.set_password(&data.master_password_hash, Some(data.key), true, None);
|
user.set_password(&data.master_password_hash, Some(data.key), true, None, &conn).await?;
|
||||||
user.password_hint = password_hint;
|
user.password_hint = password_hint;
|
||||||
|
|
||||||
// Add extra fields if present
|
// Add extra fields if present
|
||||||
@@ -364,7 +363,9 @@ async fn post_set_password(data: Json<SetPasswordData>, headers: Headers, conn:
|
|||||||
Some(data.key),
|
Some(data.key),
|
||||||
false,
|
false,
|
||||||
Some(vec![String::from("revision_date")]), // We need to allow revision-date to use the old security_timestamp
|
Some(vec![String::from("revision_date")]), // We need to allow revision-date to use the old security_timestamp
|
||||||
);
|
&conn,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
user.password_hint = password_hint;
|
user.password_hint = password_hint;
|
||||||
|
|
||||||
if let Some(keys) = data.keys {
|
if let Some(keys) = data.keys {
|
||||||
@@ -374,14 +375,12 @@ async fn post_set_password(data: Json<SetPasswordData>, headers: Headers, conn:
|
|||||||
|
|
||||||
if let Some(identifier) = data.org_identifier {
|
if let Some(identifier) = data.org_identifier {
|
||||||
if identifier != crate::sso::FAKE_IDENTIFIER && identifier != crate::api::admin::FAKE_ADMIN_UUID {
|
if identifier != crate::sso::FAKE_IDENTIFIER && identifier != crate::api::admin::FAKE_ADMIN_UUID {
|
||||||
let org = match Organization::find_by_uuid(&identifier.into(), &conn).await {
|
let Some(org) = Organization::find_by_uuid(&identifier.into(), &conn).await else {
|
||||||
None => err!("Failed to retrieve the associated organization"),
|
err!("Failed to retrieve the associated organization")
|
||||||
Some(org) => org,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let membership = match Membership::find_by_user_and_org(&user.uuid, &org.uuid, &conn).await {
|
let Some(membership) = Membership::find_by_user_and_org(&user.uuid, &org.uuid, &conn).await else {
|
||||||
None => err!("Failed to retrieve the invitation"),
|
err!("Failed to retrieve the invitation")
|
||||||
Some(org) => org,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
accept_org_invite(&user, membership, None, &conn).await?;
|
accept_org_invite(&user, membership, None, &conn).await?;
|
||||||
@@ -532,7 +531,9 @@ async fn post_password(data: Json<ChangePassData>, headers: Headers, conn: DbCon
|
|||||||
String::from("get_public_keys"),
|
String::from("get_public_keys"),
|
||||||
String::from("get_api_webauthn"),
|
String::from("get_api_webauthn"),
|
||||||
]),
|
]),
|
||||||
);
|
&conn,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|
||||||
@@ -579,7 +580,6 @@ fn set_kdf_data(user: &mut User, data: &KDFData) -> EmptyResult {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct AuthenticationData {
|
struct AuthenticationData {
|
||||||
@@ -588,7 +588,6 @@ struct AuthenticationData {
|
|||||||
master_password_authentication_hash: String,
|
master_password_authentication_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct UnlockData {
|
struct UnlockData {
|
||||||
@@ -597,11 +596,12 @@ struct UnlockData {
|
|||||||
master_key_wrapped_user_key: String,
|
master_key_wrapped_user_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct ChangeKdfData {
|
struct ChangeKdfData {
|
||||||
|
#[allow(dead_code)]
|
||||||
new_master_password_hash: String,
|
new_master_password_hash: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
key: String,
|
key: String,
|
||||||
authentication_data: AuthenticationData,
|
authentication_data: AuthenticationData,
|
||||||
unlock_data: UnlockData,
|
unlock_data: UnlockData,
|
||||||
@@ -633,7 +633,9 @@ async fn post_kdf(data: Json<ChangeKdfData>, headers: Headers, conn: DbConn, nt:
|
|||||||
Some(data.unlock_data.master_key_wrapped_user_key),
|
Some(data.unlock_data.master_key_wrapped_user_key),
|
||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
);
|
&conn,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, Some(headers.device.uuid.clone()), &conn).await;
|
nt.send_logout(&user, Some(headers.device.uuid.clone()), &conn).await;
|
||||||
@@ -900,7 +902,9 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, conn: DbConn, nt:
|
|||||||
Some(data.account_unlock_data.master_password_unlock_data.master_key_encrypted_user_key),
|
Some(data.account_unlock_data.master_password_unlock_data.master_key_encrypted_user_key),
|
||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
);
|
&conn,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|
||||||
@@ -920,7 +924,7 @@ async fn post_sstamp(data: Json<PasswordOrOtpData>, headers: Headers, conn: DbCo
|
|||||||
data.validate(&user, true, &conn).await?;
|
data.validate(&user, true, &conn).await?;
|
||||||
|
|
||||||
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
Device::delete_all_by_user(&user.uuid, &conn).await?;
|
||||||
user.reset_security_stamp();
|
user.reset_security_stamp(&conn).await?;
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|
||||||
nt.send_logout(&user, None, &conn).await;
|
nt.send_logout(&user, None, &conn).await;
|
||||||
@@ -1042,7 +1046,7 @@ async fn post_email(data: Json<ChangeEmailData>, headers: Headers, conn: DbConn,
|
|||||||
user.email_new = None;
|
user.email_new = None;
|
||||||
user.email_new_token = None;
|
user.email_new_token = None;
|
||||||
|
|
||||||
user.set_password(&data.new_master_password_hash, Some(data.key), true, None);
|
user.set_password(&data.new_master_password_hash, Some(data.key), true, None, &conn).await?;
|
||||||
|
|
||||||
let save_result = user.save(&conn).await;
|
let save_result = user.save(&conn).await;
|
||||||
|
|
||||||
@@ -1254,7 +1258,7 @@ struct SecretVerificationRequest {
|
|||||||
pub async fn kdf_upgrade(user: &mut User, pwd_hash: &str, conn: &DbConn) -> ApiResult<()> {
|
pub async fn kdf_upgrade(user: &mut User, pwd_hash: &str, conn: &DbConn) -> ApiResult<()> {
|
||||||
if user.password_iterations < CONFIG.password_iterations() {
|
if user.password_iterations < CONFIG.password_iterations() {
|
||||||
user.password_iterations = CONFIG.password_iterations();
|
user.password_iterations = CONFIG.password_iterations();
|
||||||
user.set_password(pwd_hash, None, false, None);
|
user.set_password(pwd_hash, None, false, None, conn).await?;
|
||||||
|
|
||||||
if let Err(e) = user.save(conn).await {
|
if let Err(e) = user.save(conn).await {
|
||||||
error!("Error updating user: {e:#?}");
|
error!("Error updating user: {e:#?}");
|
||||||
|
|||||||
+64
-52
@@ -14,7 +14,7 @@ use crate::auth::ClientVersion;
|
|||||||
use crate::util::{save_temp_file, NumberOrString};
|
use crate::util::{save_temp_file, NumberOrString};
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{self, core::log_event, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType},
|
api::{self, core::log_event, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType},
|
||||||
auth::Headers,
|
auth::{Headers, OrgIdGuard, OwnerHeaders},
|
||||||
config::PathType,
|
config::PathType,
|
||||||
crypto,
|
crypto,
|
||||||
db::{
|
db::{
|
||||||
@@ -86,7 +86,8 @@ pub fn routes() -> Vec<Route> {
|
|||||||
restore_cipher_put_admin,
|
restore_cipher_put_admin,
|
||||||
restore_cipher_selected,
|
restore_cipher_selected,
|
||||||
restore_cipher_selected_admin,
|
restore_cipher_selected_admin,
|
||||||
delete_all,
|
purge_org_vault,
|
||||||
|
purge_personal_vault,
|
||||||
move_cipher_selected,
|
move_cipher_selected,
|
||||||
move_cipher_selected_put,
|
move_cipher_selected_put,
|
||||||
put_collections2_update,
|
put_collections2_update,
|
||||||
@@ -425,7 +426,7 @@ pub async fn update_cipher_from_data(
|
|||||||
let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some();
|
let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some();
|
||||||
|
|
||||||
if let Some(org_id) = data.organization_id {
|
if let Some(org_id) = data.organization_id {
|
||||||
match Membership::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await {
|
match Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, conn).await {
|
||||||
None => err!("You don't have permission to add item to organization"),
|
None => err!("You don't have permission to add item to organization"),
|
||||||
Some(member) => {
|
Some(member) => {
|
||||||
if shared_to_collections.is_some()
|
if shared_to_collections.is_some()
|
||||||
@@ -1642,9 +1643,51 @@ struct OrganizationIdData {
|
|||||||
org_id: OrganizationId,
|
org_id: OrganizationId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the OrgIdGuard here, to ensure there an organization id present.
|
||||||
|
// If there is no organization id present, it should be forwarded to purge_personal_vault.
|
||||||
|
// This guard needs to be the first argument, else OwnerHeaders will be triggered which will logout the user.
|
||||||
#[post("/ciphers/purge?<organization..>", data = "<data>")]
|
#[post("/ciphers/purge?<organization..>", data = "<data>")]
|
||||||
async fn delete_all(
|
async fn purge_org_vault(
|
||||||
organization: Option<OrganizationIdData>,
|
_org_id_guard: OrgIdGuard,
|
||||||
|
organization: OrganizationIdData,
|
||||||
|
data: Json<PasswordOrOtpData>,
|
||||||
|
headers: OwnerHeaders,
|
||||||
|
conn: DbConn,
|
||||||
|
nt: Notify<'_>,
|
||||||
|
) -> EmptyResult {
|
||||||
|
if organization.org_id != headers.org_id {
|
||||||
|
err!("Organization not found", "Organization id's do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
let data: PasswordOrOtpData = data.into_inner();
|
||||||
|
let user = headers.user;
|
||||||
|
|
||||||
|
data.validate(&user, true, &conn).await?;
|
||||||
|
|
||||||
|
match Membership::find_confirmed_by_user_and_org(&user.uuid, &organization.org_id, &conn).await {
|
||||||
|
Some(member) if member.atype == MembershipType::Owner => {
|
||||||
|
Cipher::delete_all_by_organization(&organization.org_id, &conn).await?;
|
||||||
|
nt.send_user_update(UpdateType::SyncVault, &user, &headers.device.push_uuid, &conn).await;
|
||||||
|
|
||||||
|
log_event(
|
||||||
|
EventType::OrganizationPurgedVault as i32,
|
||||||
|
&organization.org_id,
|
||||||
|
&organization.org_id,
|
||||||
|
&user.uuid,
|
||||||
|
headers.device.atype,
|
||||||
|
&headers.ip.ip,
|
||||||
|
&conn,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => err!("You don't have permission to purge the organization vault"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/ciphers/purge", data = "<data>")]
|
||||||
|
async fn purge_personal_vault(
|
||||||
data: Json<PasswordOrOtpData>,
|
data: Json<PasswordOrOtpData>,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
conn: DbConn,
|
conn: DbConn,
|
||||||
@@ -1655,52 +1698,18 @@ async fn delete_all(
|
|||||||
|
|
||||||
data.validate(&user, true, &conn).await?;
|
data.validate(&user, true, &conn).await?;
|
||||||
|
|
||||||
match organization {
|
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn).await {
|
||||||
Some(org_data) => {
|
cipher.delete(&conn).await?;
|
||||||
// Organization ID in query params, purging organization vault
|
|
||||||
match Membership::find_by_user_and_org(&user.uuid, &org_data.org_id, &conn).await {
|
|
||||||
None => err!("You don't have permission to purge the organization vault"),
|
|
||||||
Some(member) => {
|
|
||||||
if member.atype == MembershipType::Owner {
|
|
||||||
Cipher::delete_all_by_organization(&org_data.org_id, &conn).await?;
|
|
||||||
nt.send_user_update(UpdateType::SyncVault, &user, &headers.device.push_uuid, &conn).await;
|
|
||||||
|
|
||||||
log_event(
|
|
||||||
EventType::OrganizationPurgedVault as i32,
|
|
||||||
&org_data.org_id,
|
|
||||||
&org_data.org_id,
|
|
||||||
&user.uuid,
|
|
||||||
headers.device.atype,
|
|
||||||
&headers.ip.ip,
|
|
||||||
&conn,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
err!("You don't have permission to purge the organization vault");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// No organization ID in query params, purging user vault
|
|
||||||
// Delete ciphers and their attachments
|
|
||||||
for cipher in Cipher::find_owned_by_user(&user.uuid, &conn).await {
|
|
||||||
cipher.delete(&conn).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete folders
|
|
||||||
for f in Folder::find_by_user(&user.uuid, &conn).await {
|
|
||||||
f.delete(&conn).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.update_revision(&conn).await?;
|
|
||||||
nt.send_user_update(UpdateType::SyncVault, &user, &headers.device.push_uuid, &conn).await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for f in Folder::find_by_user(&user.uuid, &conn).await {
|
||||||
|
f.delete(&conn).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.update_revision(&conn).await?;
|
||||||
|
nt.send_user_update(UpdateType::SyncVault, &user, &headers.device.push_uuid, &conn).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
@@ -1980,8 +1989,11 @@ impl CipherSyncData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate a HashMap with the Organization UUID as key and the Membership record
|
// Generate a HashMap with the Organization UUID as key and the Membership record
|
||||||
let members: HashMap<OrganizationId, Membership> =
|
let members: HashMap<OrganizationId, Membership> = Membership::find_confirmed_by_user(user_id, conn)
|
||||||
Membership::find_by_user(user_id, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| (m.org_uuid.clone(), m))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
|
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
|
||||||
let user_collections: HashMap<CollectionId, CollectionUser> = CollectionUser::find_by_user(user_id, conn)
|
let user_collections: HashMap<CollectionId, CollectionUser> = CollectionUser::find_by_user(user_id, conn)
|
||||||
|
|||||||
@@ -653,7 +653,7 @@ async fn password_emergency_access(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// change grantor_user password
|
// change grantor_user password
|
||||||
grantor_user.set_password(new_master_password_hash, Some(data.key), true, None);
|
grantor_user.set_password(new_master_password_hash, Some(data.key), true, None, &conn).await?;
|
||||||
grantor_user.save(&conn).await?;
|
grantor_user.save(&conn).await?;
|
||||||
|
|
||||||
// Disable TwoFactor providers since they will otherwise block logins
|
// Disable TwoFactor providers since they will otherwise block logins
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ async fn _log_user_event(
|
|||||||
ip: &IpAddr,
|
ip: &IpAddr,
|
||||||
conn: &DbConn,
|
conn: &DbConn,
|
||||||
) {
|
) {
|
||||||
let memberships = Membership::find_by_user(user_id, conn).await;
|
let memberships = Membership::find_confirmed_by_user(user_id, conn).await;
|
||||||
let mut events: Vec<Event> = Vec::with_capacity(memberships.len() + 1); // We need an event per org and one without an org
|
let mut events: Vec<Event> = Vec::with_capacity(memberships.len() + 1); // We need an event per org and one without an org
|
||||||
|
|
||||||
// Upstream saves the event also without any org_id.
|
// Upstream saves the event also without any org_id.
|
||||||
|
|||||||
+131
-60
@@ -131,6 +131,24 @@ struct FullCollectionData {
|
|||||||
external_id: Option<String>,
|
external_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FullCollectionData {
|
||||||
|
pub async fn validate(&self, org_id: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
|
let org_groups = Group::find_by_organization(org_id, conn).await;
|
||||||
|
let org_group_ids: HashSet<&GroupId> = org_groups.iter().map(|c| &c.uuid).collect();
|
||||||
|
if let Some(e) = self.groups.iter().find(|g| !org_group_ids.contains(&g.id)) {
|
||||||
|
err!("Invalid group", format!("Group {} does not belong to organization {}!", e.id, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
let org_memberships = Membership::find_by_org(org_id, conn).await;
|
||||||
|
let org_membership_ids: HashSet<&MembershipId> = org_memberships.iter().map(|m| &m.uuid).collect();
|
||||||
|
if let Some(e) = self.users.iter().find(|m| !org_membership_ids.contains(&m.id)) {
|
||||||
|
err!("Invalid member", format!("Member {} does not belong to organization {}!", e.id, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct CollectionGroupData {
|
struct CollectionGroupData {
|
||||||
@@ -233,30 +251,30 @@ async fn post_delete_organization(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/organizations/<org_id>/leave")]
|
#[post("/organizations/<org_id>/leave")]
|
||||||
async fn leave_organization(org_id: OrganizationId, headers: Headers, conn: DbConn) -> EmptyResult {
|
async fn leave_organization(org_id: OrganizationId, headers: OrgMemberHeaders, conn: DbConn) -> EmptyResult {
|
||||||
match Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await {
|
if headers.membership.status != MembershipStatus::Confirmed as i32 {
|
||||||
None => err!("User not part of organization"),
|
err!("You need to be a Member of the Organization to call this endpoint")
|
||||||
Some(member) => {
|
|
||||||
if member.atype == MembershipType::Owner
|
|
||||||
&& Membership::count_confirmed_by_org_and_type(&org_id, MembershipType::Owner, &conn).await <= 1
|
|
||||||
{
|
|
||||||
err!("The last owner can't leave")
|
|
||||||
}
|
|
||||||
|
|
||||||
log_event(
|
|
||||||
EventType::OrganizationUserLeft as i32,
|
|
||||||
&member.uuid,
|
|
||||||
&org_id,
|
|
||||||
&headers.user.uuid,
|
|
||||||
headers.device.atype,
|
|
||||||
&headers.ip.ip,
|
|
||||||
&conn,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
member.delete(&conn).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let membership = headers.membership;
|
||||||
|
|
||||||
|
if membership.atype == MembershipType::Owner
|
||||||
|
&& Membership::count_confirmed_by_org_and_type(&org_id, MembershipType::Owner, &conn).await <= 1
|
||||||
|
{
|
||||||
|
err!("The last owner can't leave")
|
||||||
|
}
|
||||||
|
|
||||||
|
log_event(
|
||||||
|
EventType::OrganizationUserLeft as i32,
|
||||||
|
&membership.uuid,
|
||||||
|
&org_id,
|
||||||
|
&headers.user.uuid,
|
||||||
|
headers.device.atype,
|
||||||
|
&headers.ip.ip,
|
||||||
|
&conn,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
membership.delete(&conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<org_id>")]
|
#[get("/organizations/<org_id>")]
|
||||||
@@ -480,12 +498,9 @@ async fn post_organization_collections(
|
|||||||
err!("Organization not found", "Organization id's do not match");
|
err!("Organization not found", "Organization id's do not match");
|
||||||
}
|
}
|
||||||
let data: FullCollectionData = data.into_inner();
|
let data: FullCollectionData = data.into_inner();
|
||||||
|
data.validate(&org_id, &conn).await?;
|
||||||
|
|
||||||
let Some(org) = Organization::find_by_uuid(&org_id, &conn).await else {
|
let collection = Collection::new(org_id.clone(), data.name, data.external_id);
|
||||||
err!("Can't find organization details")
|
|
||||||
};
|
|
||||||
|
|
||||||
let collection = Collection::new(org.uuid, data.name, data.external_id);
|
|
||||||
collection.save(&conn).await?;
|
collection.save(&conn).await?;
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
@@ -501,7 +516,7 @@ async fn post_organization_collections(
|
|||||||
|
|
||||||
for group in data.groups {
|
for group in data.groups {
|
||||||
CollectionGroup::new(collection.uuid.clone(), group.id, group.read_only, group.hide_passwords, group.manage)
|
CollectionGroup::new(collection.uuid.clone(), group.id, group.read_only, group.hide_passwords, group.manage)
|
||||||
.save(&conn)
|
.save(&org_id, &conn)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,10 +594,10 @@ async fn post_bulk_access_collections(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
CollectionGroup::delete_all_by_collection(&col_id, &conn).await?;
|
CollectionGroup::delete_all_by_collection(&col_id, &org_id, &conn).await?;
|
||||||
for group in &data.groups {
|
for group in &data.groups {
|
||||||
CollectionGroup::new(col_id.clone(), group.id.clone(), group.read_only, group.hide_passwords, group.manage)
|
CollectionGroup::new(col_id.clone(), group.id.clone(), group.read_only, group.hide_passwords, group.manage)
|
||||||
.save(&conn)
|
.save(&org_id, &conn)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,6 +642,7 @@ async fn post_organization_collection_update(
|
|||||||
err!("Organization not found", "Organization id's do not match");
|
err!("Organization not found", "Organization id's do not match");
|
||||||
}
|
}
|
||||||
let data: FullCollectionData = data.into_inner();
|
let data: FullCollectionData = data.into_inner();
|
||||||
|
data.validate(&org_id, &conn).await?;
|
||||||
|
|
||||||
if Organization::find_by_uuid(&org_id, &conn).await.is_none() {
|
if Organization::find_by_uuid(&org_id, &conn).await.is_none() {
|
||||||
err!("Can't find organization details")
|
err!("Can't find organization details")
|
||||||
@@ -655,11 +671,11 @@ async fn post_organization_collection_update(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
CollectionGroup::delete_all_by_collection(&col_id, &conn).await?;
|
CollectionGroup::delete_all_by_collection(&col_id, &org_id, &conn).await?;
|
||||||
|
|
||||||
for group in data.groups {
|
for group in data.groups {
|
||||||
CollectionGroup::new(col_id.clone(), group.id, group.read_only, group.hide_passwords, group.manage)
|
CollectionGroup::new(col_id.clone(), group.id, group.read_only, group.hide_passwords, group.manage)
|
||||||
.save(&conn)
|
.save(&org_id, &conn)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,6 +1019,24 @@ struct InviteData {
|
|||||||
permissions: HashMap<String, Value>,
|
permissions: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InviteData {
|
||||||
|
async fn validate(&self, org_id: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
|
let org_collections = Collection::find_by_organization(org_id, conn).await;
|
||||||
|
let org_collection_ids: HashSet<&CollectionId> = org_collections.iter().map(|c| &c.uuid).collect();
|
||||||
|
if let Some(e) = self.collections.iter().flatten().find(|c| !org_collection_ids.contains(&c.id)) {
|
||||||
|
err!("Invalid collection", format!("Collection {} does not belong to organization {}!", e.id, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
let org_groups = Group::find_by_organization(org_id, conn).await;
|
||||||
|
let org_group_ids: HashSet<&GroupId> = org_groups.iter().map(|c| &c.uuid).collect();
|
||||||
|
if let Some(e) = self.groups.iter().find(|g| !org_group_ids.contains(g)) {
|
||||||
|
err!("Invalid group", format!("Group {} does not belong to organization {}!", e, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/organizations/<org_id>/users/invite", data = "<data>")]
|
#[post("/organizations/<org_id>/users/invite", data = "<data>")]
|
||||||
async fn send_invite(
|
async fn send_invite(
|
||||||
org_id: OrganizationId,
|
org_id: OrganizationId,
|
||||||
@@ -1014,6 +1048,7 @@ async fn send_invite(
|
|||||||
err!("Organization not found", "Organization id's do not match");
|
err!("Organization not found", "Organization id's do not match");
|
||||||
}
|
}
|
||||||
let data: InviteData = data.into_inner();
|
let data: InviteData = data.into_inner();
|
||||||
|
data.validate(&org_id, &conn).await?;
|
||||||
|
|
||||||
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
// HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission
|
||||||
// The from_str() will convert the custom role type into a manager role type
|
// The from_str() will convert the custom role type into a manager role type
|
||||||
@@ -1273,20 +1308,20 @@ async fn accept_invite(
|
|||||||
|
|
||||||
// skip invitation logic when we were invited via the /admin panel
|
// skip invitation logic when we were invited via the /admin panel
|
||||||
if **member_id != FAKE_ADMIN_UUID {
|
if **member_id != FAKE_ADMIN_UUID {
|
||||||
let Some(mut member) = Membership::find_by_uuid_and_org(member_id, &claims.org_id, &conn).await else {
|
let Some(mut membership) = Membership::find_by_uuid_and_org(member_id, &claims.org_id, &conn).await else {
|
||||||
err!("Error accepting the invitation")
|
err!("Error accepting the invitation")
|
||||||
};
|
};
|
||||||
|
|
||||||
let reset_password_key = match OrgPolicy::org_is_reset_password_auto_enroll(&member.org_uuid, &conn).await {
|
let reset_password_key = match OrgPolicy::org_is_reset_password_auto_enroll(&membership.org_uuid, &conn).await {
|
||||||
true if data.reset_password_key.is_none() => err!("Reset password key is required, but not provided."),
|
true if data.reset_password_key.is_none() => err!("Reset password key is required, but not provided."),
|
||||||
true => data.reset_password_key,
|
true => data.reset_password_key,
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// In case the user was invited before the mail was saved in db.
|
// In case the user was invited before the mail was saved in db.
|
||||||
member.invited_by_email = member.invited_by_email.or(claims.invited_by_email);
|
membership.invited_by_email = membership.invited_by_email.or(claims.invited_by_email);
|
||||||
|
|
||||||
accept_org_invite(&headers.user, member, reset_password_key, &conn).await?;
|
accept_org_invite(&headers.user, membership, reset_password_key, &conn).await?;
|
||||||
} else if CONFIG.mail_enabled() {
|
} else if CONFIG.mail_enabled() {
|
||||||
// User was invited from /admin, so they are automatically confirmed
|
// User was invited from /admin, so they are automatically confirmed
|
||||||
let org_name = CONFIG.invitation_org_name();
|
let org_name = CONFIG.invitation_org_name();
|
||||||
@@ -1520,9 +1555,8 @@ async fn edit_member(
|
|||||||
&& data.permissions.get("deleteAnyCollection") == Some(&json!(true))
|
&& data.permissions.get("deleteAnyCollection") == Some(&json!(true))
|
||||||
&& data.permissions.get("createNewCollections") == Some(&json!(true)));
|
&& data.permissions.get("createNewCollections") == Some(&json!(true)));
|
||||||
|
|
||||||
let mut member_to_edit = match Membership::find_by_uuid_and_org(&member_id, &org_id, &conn).await {
|
let Some(mut member_to_edit) = Membership::find_by_uuid_and_org(&member_id, &org_id, &conn).await else {
|
||||||
Some(member) => member,
|
err!("The specified user isn't member of the organization")
|
||||||
None => err!("The specified user isn't member of the organization"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if new_type != member_to_edit.atype
|
if new_type != member_to_edit.atype
|
||||||
@@ -1839,7 +1873,6 @@ async fn post_org_import(
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[allow(dead_code)]
|
|
||||||
struct BulkCollectionsData {
|
struct BulkCollectionsData {
|
||||||
organization_id: OrganizationId,
|
organization_id: OrganizationId,
|
||||||
cipher_ids: Vec<CipherId>,
|
cipher_ids: Vec<CipherId>,
|
||||||
@@ -1853,6 +1886,10 @@ struct BulkCollectionsData {
|
|||||||
async fn post_bulk_collections(data: Json<BulkCollectionsData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
async fn post_bulk_collections(data: Json<BulkCollectionsData>, headers: Headers, conn: DbConn) -> EmptyResult {
|
||||||
let data: BulkCollectionsData = data.into_inner();
|
let data: BulkCollectionsData = data.into_inner();
|
||||||
|
|
||||||
|
if Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &data.organization_id, &conn).await.is_none() {
|
||||||
|
err!("You need to be a Member of the Organization to call this endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
// Get all the collection available to the user in one query
|
// Get all the collection available to the user in one query
|
||||||
// Also filter based upon the provided collections
|
// Also filter based upon the provided collections
|
||||||
let user_collections: HashMap<CollectionId, Collection> =
|
let user_collections: HashMap<CollectionId, Collection> =
|
||||||
@@ -1941,7 +1978,7 @@ async fn list_policies_token(org_id: OrganizationId, token: &str, conn: DbConn)
|
|||||||
// Called during the SSO enrollment.
|
// Called during the SSO enrollment.
|
||||||
// Return the org policy if it exists, otherwise use the default one.
|
// Return the org policy if it exists, otherwise use the default one.
|
||||||
#[get("/organizations/<org_id>/policies/master-password", rank = 1)]
|
#[get("/organizations/<org_id>/policies/master-password", rank = 1)]
|
||||||
async fn get_master_password_policy(org_id: OrganizationId, _headers: Headers, conn: DbConn) -> JsonResult {
|
async fn get_master_password_policy(org_id: OrganizationId, _headers: OrgMemberHeaders, conn: DbConn) -> JsonResult {
|
||||||
let policy =
|
let policy =
|
||||||
OrgPolicy::find_by_org_and_type(&org_id, OrgPolicyType::MasterPassword, &conn).await.unwrap_or_else(|| {
|
OrgPolicy::find_by_org_and_type(&org_id, OrgPolicyType::MasterPassword, &conn).await.unwrap_or_else(|| {
|
||||||
let (enabled, data) = match CONFIG.sso_master_password_policy_value() {
|
let (enabled, data) = match CONFIG.sso_master_password_policy_value() {
|
||||||
@@ -2149,13 +2186,13 @@ fn get_plans() -> Json<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<_org_id>/billing/metadata")]
|
#[get("/organizations/<_org_id>/billing/metadata")]
|
||||||
fn get_billing_metadata(_org_id: OrganizationId, _headers: Headers) -> Json<Value> {
|
fn get_billing_metadata(_org_id: OrganizationId, _headers: OrgMemberHeaders) -> Json<Value> {
|
||||||
// Prevent a 404 error, which also causes Javascript errors.
|
// Prevent a 404 error, which also causes Javascript errors.
|
||||||
Json(_empty_data_json())
|
Json(_empty_data_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/organizations/<_org_id>/billing/vnext/warnings")]
|
#[get("/organizations/<_org_id>/billing/vnext/warnings")]
|
||||||
fn get_billing_warnings(_org_id: OrganizationId, _headers: Headers) -> Json<Value> {
|
fn get_billing_warnings(_org_id: OrganizationId, _headers: OrgMemberHeaders) -> Json<Value> {
|
||||||
Json(json!({
|
Json(json!({
|
||||||
"freeTrial":null,
|
"freeTrial":null,
|
||||||
"inactiveSubscription":null,
|
"inactiveSubscription":null,
|
||||||
@@ -2427,6 +2464,23 @@ impl GroupRequest {
|
|||||||
|
|
||||||
group
|
group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate if all the collections and members belong to the provided organization
|
||||||
|
pub async fn validate(&self, org_id: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
|
let org_collections = Collection::find_by_organization(org_id, conn).await;
|
||||||
|
let org_collection_ids: HashSet<&CollectionId> = org_collections.iter().map(|c| &c.uuid).collect();
|
||||||
|
if let Some(e) = self.collections.iter().find(|c| !org_collection_ids.contains(&c.id)) {
|
||||||
|
err!("Invalid collection", format!("Collection {} does not belong to organization {}!", e.id, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
let org_memberships = Membership::find_by_org(org_id, conn).await;
|
||||||
|
let org_membership_ids: HashSet<&MembershipId> = org_memberships.iter().map(|m| &m.uuid).collect();
|
||||||
|
if let Some(e) = self.users.iter().find(|m| !org_membership_ids.contains(m)) {
|
||||||
|
err!("Invalid member", format!("Member {} does not belong to organization {}!", e, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
@@ -2470,6 +2524,8 @@ async fn post_groups(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let group_request = data.into_inner();
|
let group_request = data.into_inner();
|
||||||
|
group_request.validate(&org_id, &conn).await?;
|
||||||
|
|
||||||
let group = group_request.to_group(&org_id);
|
let group = group_request.to_group(&org_id);
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
@@ -2506,10 +2562,12 @@ async fn put_group(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let group_request = data.into_inner();
|
let group_request = data.into_inner();
|
||||||
|
group_request.validate(&org_id, &conn).await?;
|
||||||
|
|
||||||
let updated_group = group_request.update_group(group);
|
let updated_group = group_request.update_group(group);
|
||||||
|
|
||||||
CollectionGroup::delete_all_by_group(&group_id, &conn).await?;
|
CollectionGroup::delete_all_by_group(&group_id, &org_id, &conn).await?;
|
||||||
GroupUser::delete_all_by_group(&group_id, &conn).await?;
|
GroupUser::delete_all_by_group(&group_id, &org_id, &conn).await?;
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
EventType::GroupUpdated as i32,
|
EventType::GroupUpdated as i32,
|
||||||
@@ -2537,7 +2595,7 @@ async fn add_update_group(
|
|||||||
|
|
||||||
for col_selection in collections {
|
for col_selection in collections {
|
||||||
let mut collection_group = col_selection.to_collection_group(group.uuid.clone());
|
let mut collection_group = col_selection.to_collection_group(group.uuid.clone());
|
||||||
collection_group.save(conn).await?;
|
collection_group.save(&org_id, conn).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for assigned_member in members {
|
for assigned_member in members {
|
||||||
@@ -2630,7 +2688,7 @@ async fn _delete_group(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
group.delete(conn).await
|
group.delete(org_id, conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/organizations/<org_id>/groups", data = "<data>")]
|
#[delete("/organizations/<org_id>/groups", data = "<data>")]
|
||||||
@@ -2689,7 +2747,7 @@ async fn get_group_members(
|
|||||||
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
|
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
|
||||||
};
|
};
|
||||||
|
|
||||||
let group_members: Vec<MembershipId> = GroupUser::find_by_group(&group_id, &conn)
|
let group_members: Vec<MembershipId> = GroupUser::find_by_group(&group_id, &org_id, &conn)
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| entry.users_organizations_uuid.clone())
|
.map(|entry| entry.users_organizations_uuid.clone())
|
||||||
@@ -2717,9 +2775,15 @@ async fn put_group_members(
|
|||||||
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
|
err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization")
|
||||||
};
|
};
|
||||||
|
|
||||||
GroupUser::delete_all_by_group(&group_id, &conn).await?;
|
|
||||||
|
|
||||||
let assigned_members = data.into_inner();
|
let assigned_members = data.into_inner();
|
||||||
|
|
||||||
|
let org_memberships = Membership::find_by_org(&org_id, &conn).await;
|
||||||
|
let org_membership_ids: HashSet<&MembershipId> = org_memberships.iter().map(|m| &m.uuid).collect();
|
||||||
|
if let Some(e) = assigned_members.iter().find(|m| !org_membership_ids.contains(m)) {
|
||||||
|
err!("Invalid member", format!("Member {} does not belong to organization {}!", e, org_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupUser::delete_all_by_group(&group_id, &org_id, &conn).await?;
|
||||||
for assigned_member in assigned_members {
|
for assigned_member in assigned_members {
|
||||||
let mut user_entry = GroupUser::new(group_id.clone(), assigned_member.clone());
|
let mut user_entry = GroupUser::new(group_id.clone(), assigned_member.clone());
|
||||||
user_entry.save(&conn).await?;
|
user_entry.save(&conn).await?;
|
||||||
@@ -2858,7 +2922,8 @@ async fn put_reset_password(
|
|||||||
let reset_request = data.into_inner();
|
let reset_request = data.into_inner();
|
||||||
|
|
||||||
let mut user = user;
|
let mut user = user;
|
||||||
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None);
|
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None, &conn)
|
||||||
|
.await?;
|
||||||
user.save(&conn).await?;
|
user.save(&conn).await?;
|
||||||
|
|
||||||
nt.send_logout(&user, None, &conn).await;
|
nt.send_logout(&user, None, &conn).await;
|
||||||
@@ -2950,15 +3015,20 @@ async fn check_reset_password_applicable(org_id: &OrganizationId, conn: &DbConn)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/organizations/<org_id>/users/<member_id>/reset-password-enrollment", data = "<data>")]
|
#[put("/organizations/<org_id>/users/<user_id>/reset-password-enrollment", data = "<data>")]
|
||||||
async fn put_reset_password_enrollment(
|
async fn put_reset_password_enrollment(
|
||||||
org_id: OrganizationId,
|
org_id: OrganizationId,
|
||||||
member_id: MembershipId,
|
user_id: UserId,
|
||||||
headers: Headers,
|
headers: OrgMemberHeaders,
|
||||||
data: Json<OrganizationUserResetPasswordEnrollmentRequest>,
|
data: Json<OrganizationUserResetPasswordEnrollmentRequest>,
|
||||||
conn: DbConn,
|
conn: DbConn,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
let Some(mut member) = Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await else {
|
if user_id != headers.user.uuid {
|
||||||
|
err!("User to enroll isn't member of required organization", "The user_id and acting user do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(mut membership) = Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, &conn).await
|
||||||
|
else {
|
||||||
err!("User to enroll isn't member of required organization")
|
err!("User to enroll isn't member of required organization")
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2985,16 +3055,17 @@ async fn put_reset_password_enrollment(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
member.reset_password_key = reset_password_key;
|
membership.reset_password_key = reset_password_key;
|
||||||
member.save(&conn).await?;
|
membership.save(&conn).await?;
|
||||||
|
|
||||||
let log_id = if member.reset_password_key.is_some() {
|
let event_type = if membership.reset_password_key.is_some() {
|
||||||
EventType::OrganizationUserResetPasswordEnroll as i32
|
EventType::OrganizationUserResetPasswordEnroll as i32
|
||||||
} else {
|
} else {
|
||||||
EventType::OrganizationUserResetPasswordWithdraw as i32
|
EventType::OrganizationUserResetPasswordWithdraw as i32
|
||||||
};
|
};
|
||||||
|
|
||||||
log_event(log_id, &member_id, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &conn).await;
|
log_event(event_type, &membership.uuid, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, conn: DbConn
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
GroupUser::delete_all_by_group(&group_uuid, &conn).await?;
|
GroupUser::delete_all_by_group(&group_uuid, &org_id, &conn).await?;
|
||||||
|
|
||||||
for ext_id in &group_data.member_external_ids {
|
for ext_id in &group_data.member_external_ids {
|
||||||
if let Some(member) = Membership::find_by_external_id_and_org(ext_id, &org_id, &conn).await {
|
if let Some(member) = Membership::find_by_external_id_and_org(ext_id, &org_id, &conn).await {
|
||||||
|
|||||||
+35
-16
@@ -704,10 +704,9 @@ pub struct OrgHeaders {
|
|||||||
|
|
||||||
impl OrgHeaders {
|
impl OrgHeaders {
|
||||||
fn is_member(&self) -> bool {
|
fn is_member(&self) -> bool {
|
||||||
// NOTE: we don't care about MembershipStatus at the moment because this is only used
|
// Only allow not revoked members, we can not use the Confirmed status here
|
||||||
// where an invited, accepted or confirmed user is expected if this ever changes or
|
// as some endpoints can be triggered by invited users during joining
|
||||||
// if from_i32 is changed to return Some(Revoked) this check needs to be changed accordingly
|
self.membership_status != MembershipStatus::Revoked && self.membership_type >= MembershipType::User
|
||||||
self.membership_type >= MembershipType::User
|
|
||||||
}
|
}
|
||||||
fn is_confirmed_and_admin(&self) -> bool {
|
fn is_confirmed_and_admin(&self) -> bool {
|
||||||
self.membership_status == MembershipStatus::Confirmed && self.membership_type >= MembershipType::Admin
|
self.membership_status == MembershipStatus::Confirmed && self.membership_type >= MembershipType::Admin
|
||||||
@@ -720,6 +719,36 @@ impl OrgHeaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// org_id is usually the second path param ("/organizations/<org_id>"),
|
||||||
|
// but there are cases where it is a query value.
|
||||||
|
// First check the path, if this is not a valid uuid, try the query values.
|
||||||
|
fn get_org_id(request: &Request<'_>) -> Option<OrganizationId> {
|
||||||
|
if let Some(Ok(org_id)) = request.param::<OrganizationId>(1) {
|
||||||
|
Some(org_id)
|
||||||
|
} else if let Some(Ok(org_id)) = request.query_value::<OrganizationId>("organizationId") {
|
||||||
|
Some(org_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Guard to ensure that there is an organization id present
|
||||||
|
// If there is no org id trigger the Outcome::Forward.
|
||||||
|
// This is useful for endpoints which work for both organization and personal vaults, like purge.
|
||||||
|
pub struct OrgIdGuard;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for OrgIdGuard {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
|
match get_org_id(request) {
|
||||||
|
Some(_) => Outcome::Success(OrgIdGuard),
|
||||||
|
None => Outcome::Forward(rocket::http::Status::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for OrgHeaders {
|
impl<'r> FromRequest<'r> for OrgHeaders {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
@@ -727,18 +756,8 @@ impl<'r> FromRequest<'r> for OrgHeaders {
|
|||||||
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let headers = try_outcome!(Headers::from_request(request).await);
|
let headers = try_outcome!(Headers::from_request(request).await);
|
||||||
|
|
||||||
// org_id is usually the second path param ("/organizations/<org_id>"),
|
// Extract the org_id from the request
|
||||||
// but there are cases where it is a query value.
|
let url_org_id = get_org_id(request);
|
||||||
// First check the path, if this is not a valid uuid, try the query values.
|
|
||||||
let url_org_id: Option<OrganizationId> = {
|
|
||||||
if let Some(Ok(org_id)) = request.param::<OrganizationId>(1) {
|
|
||||||
Some(org_id)
|
|
||||||
} else if let Some(Ok(org_id)) = request.query_value::<OrganizationId>("organizationId") {
|
|
||||||
Some(org_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match url_org_id {
|
match url_org_id {
|
||||||
Some(org_id) if uuid::Uuid::parse_str(&org_id).is_ok() => {
|
Some(org_id) if uuid::Uuid::parse_str(&org_id).is_ok() => {
|
||||||
|
|||||||
+30
-21
@@ -559,7 +559,7 @@ impl Cipher {
|
|||||||
if let Some(cached_member) = cipher_sync_data.members.get(org_uuid) {
|
if let Some(cached_member) = cipher_sync_data.members.get(org_uuid) {
|
||||||
return cached_member.has_full_access();
|
return cached_member.has_full_access();
|
||||||
}
|
}
|
||||||
} else if let Some(member) = Membership::find_by_user_and_org(user_uuid, org_uuid, conn).await {
|
} else if let Some(member) = Membership::find_confirmed_by_user_and_org(user_uuid, org_uuid, conn).await {
|
||||||
return member.has_full_access();
|
return member.has_full_access();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -668,10 +668,12 @@ impl Cipher {
|
|||||||
ciphers::table
|
ciphers::table
|
||||||
.filter(ciphers::uuid.eq(&self.uuid))
|
.filter(ciphers::uuid.eq(&self.uuid))
|
||||||
.inner_join(ciphers_collections::table.on(
|
.inner_join(ciphers_collections::table.on(
|
||||||
ciphers::uuid.eq(ciphers_collections::cipher_uuid)))
|
ciphers::uuid.eq(ciphers_collections::cipher_uuid)
|
||||||
|
))
|
||||||
.inner_join(users_collections::table.on(
|
.inner_join(users_collections::table.on(
|
||||||
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
|
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
|
||||||
.and(users_collections::user_uuid.eq(user_uuid))))
|
.and(users_collections::user_uuid.eq(user_uuid))
|
||||||
|
))
|
||||||
.select((users_collections::read_only, users_collections::hide_passwords, users_collections::manage))
|
.select((users_collections::read_only, users_collections::hide_passwords, users_collections::manage))
|
||||||
.load::<(bool, bool, bool)>(conn)
|
.load::<(bool, bool, bool)>(conn)
|
||||||
.expect("Error getting user access restrictions")
|
.expect("Error getting user access restrictions")
|
||||||
@@ -697,6 +699,9 @@ impl Cipher {
|
|||||||
.inner_join(users_organizations::table.on(
|
.inner_join(users_organizations::table.on(
|
||||||
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
||||||
))
|
))
|
||||||
|
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
.filter(users_organizations::user_uuid.eq(user_uuid))
|
.filter(users_organizations::user_uuid.eq(user_uuid))
|
||||||
.select((collections_groups::read_only, collections_groups::hide_passwords, collections_groups::manage))
|
.select((collections_groups::read_only, collections_groups::hide_passwords, collections_groups::manage))
|
||||||
.load::<(bool, bool, bool)>(conn)
|
.load::<(bool, bool, bool)>(conn)
|
||||||
@@ -795,28 +800,28 @@ impl Cipher {
|
|||||||
let mut query = ciphers::table
|
let mut query = ciphers::table
|
||||||
.left_join(ciphers_collections::table.on(
|
.left_join(ciphers_collections::table.on(
|
||||||
ciphers::uuid.eq(ciphers_collections::cipher_uuid)
|
ciphers::uuid.eq(ciphers_collections::cipher_uuid)
|
||||||
))
|
))
|
||||||
.left_join(users_organizations::table.on(
|
.left_join(users_organizations::table.on(
|
||||||
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
|
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
|
||||||
.and(users_organizations::user_uuid.eq(user_uuid))
|
.and(users_organizations::user_uuid.eq(user_uuid))
|
||||||
.and(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
|
.and(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
|
||||||
))
|
))
|
||||||
.left_join(users_collections::table.on(
|
.left_join(users_collections::table.on(
|
||||||
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
|
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
|
||||||
// Ensure that users_collections::user_uuid is NULL for unconfirmed users.
|
// Ensure that users_collections::user_uuid is NULL for unconfirmed users.
|
||||||
.and(users_organizations::user_uuid.eq(users_collections::user_uuid))
|
.and(users_organizations::user_uuid.eq(users_collections::user_uuid))
|
||||||
))
|
))
|
||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
// Ensure that group and membership belong to the same org
|
||||||
))
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and(
|
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
|
||||||
collections_groups::groups_uuid.eq(groups::uuid)
|
.and(collections_groups::groups_uuid.eq(groups::uuid))
|
||||||
)
|
))
|
||||||
))
|
|
||||||
.filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
|
.filter(ciphers::user_uuid.eq(user_uuid)) // Cipher owner
|
||||||
.or_filter(users_organizations::access_all.eq(true)) // access_all in org
|
.or_filter(users_organizations::access_all.eq(true)) // access_all in org
|
||||||
.or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
|
.or_filter(users_collections::user_uuid.eq(user_uuid)) // Access to collection
|
||||||
@@ -986,7 +991,9 @@ impl Cipher {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
|
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
|
||||||
.and(collections_groups::groups_uuid.eq(groups::uuid))
|
.and(collections_groups::groups_uuid.eq(groups::uuid))
|
||||||
@@ -1047,7 +1054,9 @@ impl Cipher {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
|
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid)
|
||||||
.and(collections_groups::groups_uuid.eq(groups::uuid))
|
.and(collections_groups::groups_uuid.eq(groups::uuid))
|
||||||
@@ -1115,8 +1124,8 @@ impl Cipher {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and(
|
collections_groups::collections_uuid.eq(ciphers_collections::collection_uuid).and(
|
||||||
|
|||||||
+11
-11
@@ -191,7 +191,7 @@ impl Collection {
|
|||||||
self.update_users_revision(conn).await;
|
self.update_users_revision(conn).await;
|
||||||
CollectionCipher::delete_all_by_collection(&self.uuid, conn).await?;
|
CollectionCipher::delete_all_by_collection(&self.uuid, conn).await?;
|
||||||
CollectionUser::delete_all_by_collection(&self.uuid, conn).await?;
|
CollectionUser::delete_all_by_collection(&self.uuid, conn).await?;
|
||||||
CollectionGroup::delete_all_by_collection(&self.uuid, conn).await?;
|
CollectionGroup::delete_all_by_collection(&self.uuid, &self.org_uuid, conn).await?;
|
||||||
|
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid)))
|
diesel::delete(collections::table.filter(collections::uuid.eq(self.uuid)))
|
||||||
@@ -239,8 +239,8 @@ impl Collection {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
||||||
@@ -355,8 +355,8 @@ impl Collection {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
||||||
@@ -422,8 +422,8 @@ impl Collection {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
||||||
@@ -484,8 +484,8 @@ impl Collection {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
||||||
@@ -531,8 +531,8 @@ impl Collection {
|
|||||||
.left_join(groups_users::table.on(
|
.left_join(groups_users::table.on(
|
||||||
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
groups_users::users_organizations_uuid.eq(users_organizations::uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid).and(
|
||||||
|
|||||||
+17
-1
@@ -49,11 +49,16 @@ impl Device {
|
|||||||
|
|
||||||
push_uuid: Some(PushId(get_uuid())),
|
push_uuid: Some(PushId(get_uuid())),
|
||||||
push_token: None,
|
push_token: None,
|
||||||
refresh_token: crypto::encode_random_bytes::<64>(&BASE64URL),
|
refresh_token: Device::generate_refresh_token(),
|
||||||
twofactor_remember: None,
|
twofactor_remember: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn generate_refresh_token() -> String {
|
||||||
|
crypto::encode_random_bytes::<64>(&BASE64URL)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_json(&self) -> Value {
|
pub fn to_json(&self) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"id": self.uuid,
|
"id": self.uuid,
|
||||||
@@ -260,6 +265,17 @@ impl Device {
|
|||||||
.unwrap_or(0) != 0
|
.unwrap_or(0) != 0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn rotate_refresh_tokens_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
|
||||||
|
// Generate a new token per device.
|
||||||
|
// We cannot do a single UPDATE with one value because each device needs a unique token.
|
||||||
|
let devices = Self::find_by_user(user_uuid, conn).await;
|
||||||
|
for mut device in devices {
|
||||||
|
device.refresh_token = Device::generate_refresh_token();
|
||||||
|
device.save(false, conn).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Display)]
|
#[derive(Display)]
|
||||||
|
|||||||
+62
-20
@@ -1,6 +1,6 @@
|
|||||||
use super::{CollectionId, Membership, MembershipId, OrganizationId, User, UserId};
|
use super::{CollectionId, Membership, MembershipId, OrganizationId, User, UserId};
|
||||||
use crate::api::EmptyResult;
|
use crate::api::EmptyResult;
|
||||||
use crate::db::schema::{collections_groups, groups, groups_users, users_organizations};
|
use crate::db::schema::{collections, collections_groups, groups, groups_users, users_organizations};
|
||||||
use crate::db::DbConn;
|
use crate::db::DbConn;
|
||||||
use crate::error::MapResult;
|
use crate::error::MapResult;
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
@@ -81,7 +81,7 @@ impl Group {
|
|||||||
// If both read_only and hide_passwords are false, then manage should be true
|
// If both read_only and hide_passwords are false, then manage should be true
|
||||||
// You can't have an entry with read_only and manage, or hide_passwords and manage
|
// You can't have an entry with read_only and manage, or hide_passwords and manage
|
||||||
// Or an entry with everything to false
|
// Or an entry with everything to false
|
||||||
let collections_groups: Vec<Value> = CollectionGroup::find_by_group(&self.uuid, conn)
|
let collections_groups: Vec<Value> = CollectionGroup::find_by_group(&self.uuid, &self.organizations_uuid, conn)
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
@@ -191,7 +191,7 @@ impl Group {
|
|||||||
|
|
||||||
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
for group in Self::find_by_organization(org_uuid, conn).await {
|
for group in Self::find_by_organization(org_uuid, conn).await {
|
||||||
group.delete(conn).await?;
|
group.delete(org_uuid, conn).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -246,8 +246,8 @@ impl Group {
|
|||||||
.inner_join(users_organizations::table.on(
|
.inner_join(users_organizations::table.on(
|
||||||
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
||||||
))
|
))
|
||||||
.inner_join(groups::table.on(
|
.inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
groups::uuid.eq(groups_users::groups_uuid)
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
))
|
))
|
||||||
.filter(users_organizations::user_uuid.eq(user_uuid))
|
.filter(users_organizations::user_uuid.eq(user_uuid))
|
||||||
.filter(groups::access_all.eq(true))
|
.filter(groups::access_all.eq(true))
|
||||||
@@ -276,9 +276,9 @@ impl Group {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
|
pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
CollectionGroup::delete_all_by_group(&self.uuid, conn).await?;
|
CollectionGroup::delete_all_by_group(&self.uuid, org_uuid, conn).await?;
|
||||||
GroupUser::delete_all_by_group(&self.uuid, conn).await?;
|
GroupUser::delete_all_by_group(&self.uuid, org_uuid, conn).await?;
|
||||||
|
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
diesel::delete(groups::table.filter(groups::uuid.eq(&self.uuid)))
|
diesel::delete(groups::table.filter(groups::uuid.eq(&self.uuid)))
|
||||||
@@ -306,8 +306,8 @@ impl Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CollectionGroup {
|
impl CollectionGroup {
|
||||||
pub async fn save(&mut self, conn: &DbConn) -> EmptyResult {
|
pub async fn save(&mut self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await;
|
let group_users = GroupUser::find_by_group(&self.groups_uuid, org_uuid, conn).await;
|
||||||
for group_user in group_users {
|
for group_user in group_users {
|
||||||
group_user.update_user_revision(conn).await;
|
group_user.update_user_revision(conn).await;
|
||||||
}
|
}
|
||||||
@@ -365,10 +365,19 @@ impl CollectionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_group(group_uuid: &GroupId, conn: &DbConn) -> Vec<Self> {
|
pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
collections_groups::table
|
collections_groups::table
|
||||||
|
.inner_join(groups::table.on(
|
||||||
|
groups::uuid.eq(collections_groups::groups_uuid)
|
||||||
|
))
|
||||||
|
.inner_join(collections::table.on(
|
||||||
|
collections::uuid.eq(collections_groups::collections_uuid)
|
||||||
|
.and(collections::org_uuid.eq(groups::organizations_uuid))
|
||||||
|
))
|
||||||
.filter(collections_groups::groups_uuid.eq(group_uuid))
|
.filter(collections_groups::groups_uuid.eq(group_uuid))
|
||||||
|
.filter(collections::org_uuid.eq(org_uuid))
|
||||||
|
.select(collections_groups::all_columns)
|
||||||
.load::<Self>(conn)
|
.load::<Self>(conn)
|
||||||
.expect("Error loading collection groups")
|
.expect("Error loading collection groups")
|
||||||
}}
|
}}
|
||||||
@@ -383,6 +392,13 @@ impl CollectionGroup {
|
|||||||
.inner_join(users_organizations::table.on(
|
.inner_join(users_organizations::table.on(
|
||||||
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
||||||
))
|
))
|
||||||
|
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
|
.inner_join(collections::table.on(
|
||||||
|
collections::uuid.eq(collections_groups::collections_uuid)
|
||||||
|
.and(collections::org_uuid.eq(groups::organizations_uuid))
|
||||||
|
))
|
||||||
.filter(users_organizations::user_uuid.eq(user_uuid))
|
.filter(users_organizations::user_uuid.eq(user_uuid))
|
||||||
.select(collections_groups::all_columns)
|
.select(collections_groups::all_columns)
|
||||||
.load::<Self>(conn)
|
.load::<Self>(conn)
|
||||||
@@ -394,14 +410,20 @@ impl CollectionGroup {
|
|||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
collections_groups::table
|
collections_groups::table
|
||||||
.filter(collections_groups::collections_uuid.eq(collection_uuid))
|
.filter(collections_groups::collections_uuid.eq(collection_uuid))
|
||||||
|
.inner_join(collections::table.on(
|
||||||
|
collections::uuid.eq(collections_groups::collections_uuid)
|
||||||
|
))
|
||||||
|
.inner_join(groups::table.on(groups::uuid.eq(collections_groups::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(collections::org_uuid))
|
||||||
|
))
|
||||||
.select(collections_groups::all_columns)
|
.select(collections_groups::all_columns)
|
||||||
.load::<Self>(conn)
|
.load::<Self>(conn)
|
||||||
.expect("Error loading collection groups")
|
.expect("Error loading collection groups")
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, conn: &DbConn) -> EmptyResult {
|
pub async fn delete(&self, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
let group_users = GroupUser::find_by_group(&self.groups_uuid, conn).await;
|
let group_users = GroupUser::find_by_group(&self.groups_uuid, org_uuid, conn).await;
|
||||||
for group_user in group_users {
|
for group_user in group_users {
|
||||||
group_user.update_user_revision(conn).await;
|
group_user.update_user_revision(conn).await;
|
||||||
}
|
}
|
||||||
@@ -415,8 +437,8 @@ impl CollectionGroup {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_all_by_group(group_uuid: &GroupId, conn: &DbConn) -> EmptyResult {
|
pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
let group_users = GroupUser::find_by_group(group_uuid, conn).await;
|
let group_users = GroupUser::find_by_group(group_uuid, org_uuid, conn).await;
|
||||||
for group_user in group_users {
|
for group_user in group_users {
|
||||||
group_user.update_user_revision(conn).await;
|
group_user.update_user_revision(conn).await;
|
||||||
}
|
}
|
||||||
@@ -429,10 +451,14 @@ impl CollectionGroup {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_all_by_collection(collection_uuid: &CollectionId, conn: &DbConn) -> EmptyResult {
|
pub async fn delete_all_by_collection(
|
||||||
|
collection_uuid: &CollectionId,
|
||||||
|
org_uuid: &OrganizationId,
|
||||||
|
conn: &DbConn,
|
||||||
|
) -> EmptyResult {
|
||||||
let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await;
|
let collection_assigned_to_groups = CollectionGroup::find_by_collection(collection_uuid, conn).await;
|
||||||
for collection_assigned_to_group in collection_assigned_to_groups {
|
for collection_assigned_to_group in collection_assigned_to_groups {
|
||||||
let group_users = GroupUser::find_by_group(&collection_assigned_to_group.groups_uuid, conn).await;
|
let group_users = GroupUser::find_by_group(&collection_assigned_to_group.groups_uuid, org_uuid, conn).await;
|
||||||
for group_user in group_users {
|
for group_user in group_users {
|
||||||
group_user.update_user_revision(conn).await;
|
group_user.update_user_revision(conn).await;
|
||||||
}
|
}
|
||||||
@@ -494,10 +520,19 @@ impl GroupUser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_by_group(group_uuid: &GroupId, conn: &DbConn) -> Vec<Self> {
|
pub async fn find_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
|
||||||
db_run! { conn: {
|
db_run! { conn: {
|
||||||
groups_users::table
|
groups_users::table
|
||||||
|
.inner_join(groups::table.on(
|
||||||
|
groups::uuid.eq(groups_users::groups_uuid)
|
||||||
|
))
|
||||||
|
.inner_join(users_organizations::table.on(
|
||||||
|
users_organizations::uuid.eq(groups_users::users_organizations_uuid)
|
||||||
|
.and(users_organizations::org_uuid.eq(groups::organizations_uuid))
|
||||||
|
))
|
||||||
.filter(groups_users::groups_uuid.eq(group_uuid))
|
.filter(groups_users::groups_uuid.eq(group_uuid))
|
||||||
|
.filter(groups::organizations_uuid.eq(org_uuid))
|
||||||
|
.select(groups_users::all_columns)
|
||||||
.load::<Self>(conn)
|
.load::<Self>(conn)
|
||||||
.expect("Error loading group users")
|
.expect("Error loading group users")
|
||||||
}}
|
}}
|
||||||
@@ -522,6 +557,13 @@ impl GroupUser {
|
|||||||
.inner_join(collections_groups::table.on(
|
.inner_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
||||||
))
|
))
|
||||||
|
.inner_join(groups::table.on(
|
||||||
|
groups::uuid.eq(groups_users::groups_uuid)
|
||||||
|
))
|
||||||
|
.inner_join(collections::table.on(
|
||||||
|
collections::uuid.eq(collections_groups::collections_uuid)
|
||||||
|
.and(collections::org_uuid.eq(groups::organizations_uuid))
|
||||||
|
))
|
||||||
.filter(collections_groups::collections_uuid.eq(collection_uuid))
|
.filter(collections_groups::collections_uuid.eq(collection_uuid))
|
||||||
.filter(groups_users::users_organizations_uuid.eq(member_uuid))
|
.filter(groups_users::users_organizations_uuid.eq(member_uuid))
|
||||||
.count()
|
.count()
|
||||||
@@ -575,8 +617,8 @@ impl GroupUser {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_all_by_group(group_uuid: &GroupId, conn: &DbConn) -> EmptyResult {
|
pub async fn delete_all_by_group(group_uuid: &GroupId, org_uuid: &OrganizationId, conn: &DbConn) -> EmptyResult {
|
||||||
let group_users = GroupUser::find_by_group(group_uuid, conn).await;
|
let group_users = GroupUser::find_by_group(group_uuid, org_uuid, conn).await;
|
||||||
for group_user in group_users {
|
for group_user in group_users {
|
||||||
group_user.update_user_revision(conn).await;
|
group_user.update_user_revision(conn).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ impl OrgPolicy {
|
|||||||
for policy in
|
for policy in
|
||||||
OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await
|
OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await
|
||||||
{
|
{
|
||||||
if let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await {
|
if let Some(user) = Membership::find_confirmed_by_user_and_org(user_uuid, &policy.org_uuid, conn).await {
|
||||||
if user.atype < MembershipType::Admin {
|
if user.atype < MembershipType::Admin {
|
||||||
match serde_json::from_str::<SendOptionsPolicyData>(&policy.data) {
|
match serde_json::from_str::<SendOptionsPolicyData>(&policy.data) {
|
||||||
Ok(opts) => {
|
Ok(opts) => {
|
||||||
|
|||||||
@@ -1073,7 +1073,9 @@ impl Membership {
|
|||||||
.left_join(collections_groups::table.on(
|
.left_join(collections_groups::table.on(
|
||||||
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
collections_groups::groups_uuid.eq(groups_users::groups_uuid)
|
||||||
))
|
))
|
||||||
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
|
.left_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)
|
||||||
|
.and(groups::organizations_uuid.eq(users_organizations::org_uuid))
|
||||||
|
))
|
||||||
.left_join(ciphers_collections::table.on(
|
.left_join(ciphers_collections::table.on(
|
||||||
ciphers_collections::collection_uuid.eq(collections_groups::collections_uuid).and(ciphers_collections::cipher_uuid.eq(&cipher_uuid))
|
ciphers_collections::collection_uuid.eq(collections_groups::collections_uuid).and(ciphers_collections::cipher_uuid.eq(&cipher_uuid))
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ pub struct TwoFactor {
|
|||||||
pub last_used: i64,
|
pub last_used: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(num_derive::FromPrimitive)]
|
#[derive(num_derive::FromPrimitive)]
|
||||||
pub enum TwoFactorType {
|
pub enum TwoFactorType {
|
||||||
Authenticator = 0,
|
Authenticator = 0,
|
||||||
|
|||||||
@@ -185,13 +185,14 @@ impl User {
|
|||||||
/// These routes are able to use the previous stamp id for the next 2 minutes.
|
/// These routes are able to use the previous stamp id for the next 2 minutes.
|
||||||
/// After these 2 minutes this stamp will expire.
|
/// After these 2 minutes this stamp will expire.
|
||||||
///
|
///
|
||||||
pub fn set_password(
|
pub async fn set_password(
|
||||||
&mut self,
|
&mut self,
|
||||||
password: &str,
|
password: &str,
|
||||||
new_key: Option<String>,
|
new_key: Option<String>,
|
||||||
reset_security_stamp: bool,
|
reset_security_stamp: bool,
|
||||||
allow_next_route: Option<Vec<String>>,
|
allow_next_route: Option<Vec<String>>,
|
||||||
) {
|
conn: &DbConn,
|
||||||
|
) -> EmptyResult {
|
||||||
self.password_hash = crypto::hash_password(password.as_bytes(), &self.salt, self.password_iterations as u32);
|
self.password_hash = crypto::hash_password(password.as_bytes(), &self.salt, self.password_iterations as u32);
|
||||||
|
|
||||||
if let Some(route) = allow_next_route {
|
if let Some(route) = allow_next_route {
|
||||||
@@ -203,12 +204,15 @@ impl User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if reset_security_stamp {
|
if reset_security_stamp {
|
||||||
self.reset_security_stamp()
|
self.reset_security_stamp(conn).await?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_security_stamp(&mut self) {
|
pub async fn reset_security_stamp(&mut self, conn: &DbConn) -> EmptyResult {
|
||||||
self.security_stamp = get_uuid();
|
self.security_stamp = get_uuid();
|
||||||
|
Device::rotate_refresh_tokens_by_user(&self.uuid, conn).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the stamp_exception to only allow a subsequent request matching a specific route using the current security-stamp.
|
/// Set the stamp_exception to only allow a subsequent request matching a specific route using the current security-stamp.
|
||||||
|
|||||||
@@ -111,7 +111,8 @@
|
|||||||
"microsoftstore.com",
|
"microsoftstore.com",
|
||||||
"xbox.com",
|
"xbox.com",
|
||||||
"azure.com",
|
"azure.com",
|
||||||
"windowsazure.com"
|
"windowsazure.com",
|
||||||
|
"cloud.microsoft"
|
||||||
],
|
],
|
||||||
"excluded": false
|
"excluded": false
|
||||||
},
|
},
|
||||||
@@ -971,5 +972,13 @@
|
|||||||
"pinterest.se"
|
"pinterest.se"
|
||||||
],
|
],
|
||||||
"excluded": false
|
"excluded": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 91,
|
||||||
|
"domains": [
|
||||||
|
"twitter.com",
|
||||||
|
"x.com"
|
||||||
|
],
|
||||||
|
"excluded": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -79,3 +79,4 @@ for name, domain_list in domain_lists.items():
|
|||||||
# Write out the global domains JSON file.
|
# Write out the global domains JSON file.
|
||||||
with open(file=OUTPUT_FILE, mode='w', encoding='utf-8') as f:
|
with open(file=OUTPUT_FILE, mode='w', encoding='utf-8') as f:
|
||||||
json.dump(global_domains, f, indent=2)
|
json.dump(global_domains, f, indent=2)
|
||||||
|
f.write("\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user