Compare commits
4 Commits
20251028
...
f64f01a958
| Author | SHA1 | Date | |
|---|---|---|---|
| f64f01a958 | |||
| 96ffc669d7 | |||
| d9f659ce98 | |||
| e73b5b8e5b |
52
Cargo.lock
generated
52
Cargo.lock
generated
@@ -317,9 +317,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.20"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -664,9 +664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.12.0"
|
version = "3.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
@@ -737,9 +737,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.2.2"
|
version = "4.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a"
|
checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -748,9 +748,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.2.2"
|
version = "4.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6"
|
checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -866,9 +866,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
|
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -1283,9 +1283,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.17"
|
version = "0.3.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f"
|
checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
@@ -1641,9 +1641,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.141"
|
version = "0.2.142"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
@@ -1662,9 +1662,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.1"
|
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 = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "local-channel"
|
name = "local-channel"
|
||||||
@@ -1875,9 +1875,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.50"
|
version = "0.10.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1"
|
checksum = "97ea2d98598bf9ada7ea6ee8a30fb74f9156b63bbe495d64ec2b87c269d2dda3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -1901,9 +1901,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.85"
|
version = "0.9.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0"
|
checksum = "992bac49bdbab4423199c654a5515bd2a6c6a23bf03f2dd3bdb7e5ae6259bc69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2135,9 +2135,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.7.3"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -2146,9 +2146,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.29"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rfc6979"
|
name = "rfc6979"
|
||||||
@@ -2217,9 +2217,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.11"
|
version = "0.37.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
|
checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
|||||||
12
assets/img/brands/gitea.svg
Normal file
12
assets/img/brands/gitea.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve"><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-custom-link"/><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-general-link"/><style xmlns="" lang="en" type="text/css" id="dark-mode-custom-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-sheet"/>
|
||||||
|
<g>
|
||||||
|
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||||
|
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
1
assets/img/brands/github.svg
Normal file
1
assets/img/brands/github.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 960 B |
1
assets/img/brands/gitlab.svg
Normal file
1
assets/img/brands/gitlab.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/><path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
assets/img/brands/google.svg
Normal file
1
assets/img/brands/google.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="705.6" height="720" viewBox="0 0 186.69 190.5" xmlns:v="https://vecta.io/nano"><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-custom-link"/><link xmlns="" type="text/css" rel="stylesheet" id="dark-mode-general-link"/><style xmlns="" lang="en" type="text/css" id="dark-mode-custom-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-style"/><style xmlns="" lang="en" type="text/css" id="dark-mode-native-sheet"/><g transform="translate(1184.583 765.171)"><path clip-path="none" mask="none" d="M-1089.333-687.239v36.888h51.262c-2.251 11.863-9.006 21.908-19.137 28.662l30.913 23.986c18.011-16.625 28.402-41.044 28.402-70.052 0-6.754-.606-13.249-1.732-19.483z" fill="#4285f4"/><path clip-path="none" mask="none" d="M-1142.714-651.791l-6.972 5.337-24.679 19.223h0c15.673 31.086 47.796 52.561 85.03 52.561 25.717 0 47.278-8.486 63.038-23.033l-30.913-23.986c-8.486 5.715-19.31 9.179-32.125 9.179-24.765 0-45.806-16.712-53.34-39.226z" fill="#34a853"/><path clip-path="none" mask="none" d="M-1174.365-712.61c-6.494 12.815-10.217 27.276-10.217 42.689s3.723 29.874 10.217 42.689c0 .086 31.693-24.592 31.693-24.592-1.905-5.715-3.031-11.776-3.031-18.098s1.126-12.383 3.031-18.098z" fill="#fbbc05"/><path d="M-1089.333-727.244c14.028 0 26.497 4.849 36.455 14.201l27.276-27.276c-16.539-15.413-38.013-24.852-63.731-24.852-37.234 0-69.359 21.388-85.032 52.561l31.692 24.592c7.533-22.514 28.575-39.226 53.34-39.226z" fill="#ea4335" clip-path="none" mask="none"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
1
assets/img/brands/microsoft.svg
Normal file
1
assets/img/brands/microsoft.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><path fill="#f35325" d="M0 0h10v10H0z"/><path fill="#81bc06" d="M11 0h10v10H11z"/><path fill="#05a6f0" d="M0 11h10v10H0z"/><path fill="#ffba08" d="M11 11h10v10H11z"/></svg>
|
||||||
|
After Width: | Height: | Size: 232 B |
@@ -19,6 +19,11 @@ pub trait UsersSyncBackend {
|
|||||||
fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> Res;
|
fn save_new_successful_2fa_authentication(&mut self, id: &UserID, ip: IpAddr) -> Res;
|
||||||
fn clear_2fa_login_history(&mut self, id: &UserID) -> Res;
|
fn clear_2fa_login_history(&mut self, id: &UserID) -> Res;
|
||||||
fn delete_account(&mut self, id: &UserID) -> Res;
|
fn delete_account(&mut self, id: &UserID) -> Res;
|
||||||
|
fn set_authorized_authentication_sources(
|
||||||
|
&mut self,
|
||||||
|
id: &UserID,
|
||||||
|
sources: AuthorizedAuthenticationSources,
|
||||||
|
) -> Res;
|
||||||
fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res;
|
fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +33,13 @@ pub enum LoginResult {
|
|||||||
AccountNotFound,
|
AccountNotFound,
|
||||||
InvalidPassword,
|
InvalidPassword,
|
||||||
AccountDisabled,
|
AccountDisabled,
|
||||||
|
LocalAuthForbidden,
|
||||||
Success(Box<User>),
|
Success(Box<User>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(LoginResult)]
|
#[rtype(LoginResult)]
|
||||||
pub struct LoginRequest {
|
pub struct LocalLoginRequest {
|
||||||
pub login: String,
|
pub login: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
@@ -88,6 +94,15 @@ pub struct AddSuccessful2FALogin(pub UserID, pub IpAddr);
|
|||||||
#[rtype(result = "bool")]
|
#[rtype(result = "bool")]
|
||||||
pub struct Clear2FALoginHistory(pub UserID);
|
pub struct Clear2FALoginHistory(pub UserID);
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||||
|
pub struct AuthorizedAuthenticationSources {
|
||||||
|
pub local: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "bool")]
|
||||||
|
pub struct SetAuthorizedAuthenticationSources(pub UserID, pub AuthorizedAuthenticationSources);
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "bool")]
|
#[rtype(result = "bool")]
|
||||||
pub struct SetGrantedClients(pub UserID, pub GrantedClients);
|
pub struct SetGrantedClients(pub UserID, pub GrantedClients);
|
||||||
@@ -119,10 +134,10 @@ impl Actor for UsersActor {
|
|||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler<LoginRequest> for UsersActor {
|
impl Handler<LocalLoginRequest> for UsersActor {
|
||||||
type Result = MessageResult<LoginRequest>;
|
type Result = MessageResult<LocalLoginRequest>;
|
||||||
|
|
||||||
fn handle(&mut self, msg: LoginRequest, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: LocalLoginRequest, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
match self.manager.find_by_username_or_email(&msg.login) {
|
match self.manager.find_by_username_or_email(&msg.login) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to find user! {}", e);
|
log::error!("Failed to find user! {}", e);
|
||||||
@@ -142,6 +157,10 @@ impl Handler<LoginRequest> for UsersActor {
|
|||||||
return MessageResult(LoginResult::AccountDisabled);
|
return MessageResult(LoginResult::AccountDisabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !user.allow_local_login {
|
||||||
|
return MessageResult(LoginResult::LocalAuthForbidden);
|
||||||
|
}
|
||||||
|
|
||||||
MessageResult(LoginResult::Success(Box::new(user)))
|
MessageResult(LoginResult::Success(Box::new(user)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +260,29 @@ impl Handler<Clear2FALoginHistory> for UsersActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Handler<SetAuthorizedAuthenticationSources> for UsersActor {
|
||||||
|
type Result = <SetAuthorizedAuthenticationSources as actix::Message>::Result;
|
||||||
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
msg: SetAuthorizedAuthenticationSources,
|
||||||
|
_ctx: &mut Self::Context,
|
||||||
|
) -> Self::Result {
|
||||||
|
match self
|
||||||
|
.manager
|
||||||
|
.set_authorized_authentication_sources(&msg.0, msg.1)
|
||||||
|
{
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to set authorized authentication sources for user! {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler<SetGrantedClients> for UsersActor {
|
impl Handler<SetGrantedClients> for UsersActor {
|
||||||
type Result = <SetGrantedClients as actix::Message>::Result;
|
type Result = <SetGrantedClients as actix::Message>::Result;
|
||||||
fn handle(&mut self, msg: SetGrantedClients, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: SetGrantedClients, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ pub const USERS_LIST_FILE: &str = "users.json";
|
|||||||
/// File in storage containing clients list
|
/// File in storage containing clients list
|
||||||
pub const CLIENTS_LIST_FILE: &str = "clients.yaml";
|
pub const CLIENTS_LIST_FILE: &str = "clients.yaml";
|
||||||
|
|
||||||
|
/// File in storage containing providers list
|
||||||
|
pub const PROVIDERS_LIST_FILE: &str = "providers.yaml";
|
||||||
|
|
||||||
/// Default built-in credentials
|
/// Default built-in credentials
|
||||||
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
|
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
|
||||||
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
|
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ use actix_web::{web, HttpResponse, Responder};
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
use crate::actors::users_actor;
|
use crate::actors::users_actor;
|
||||||
use crate::actors::users_actor::UsersActor;
|
use crate::actors::users_actor::{AuthorizedAuthenticationSources, UsersActor};
|
||||||
use crate::constants::TEMPORARY_PASSWORDS_LEN;
|
use crate::constants::TEMPORARY_PASSWORDS_LEN;
|
||||||
use crate::controllers::settings_controller::BaseSettingsPage;
|
use crate::controllers::settings_controller::BaseSettingsPage;
|
||||||
use crate::data::action_logger::{Action, ActionLogger};
|
use crate::data::action_logger::{Action, ActionLogger};
|
||||||
use crate::data::client::{Client, ClientID, ClientManager};
|
use crate::data::client::{Client, ClientID, ClientManager};
|
||||||
use crate::data::current_user::CurrentUser;
|
use crate::data::current_user::CurrentUser;
|
||||||
|
use crate::data::provider::{Provider, ProvidersManager};
|
||||||
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
|
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
|
||||||
use crate::utils::string_utils::rand_str;
|
use crate::utils::string_utils::rand_str;
|
||||||
|
|
||||||
@@ -22,6 +23,13 @@ struct ClientsListTemplate {
|
|||||||
clients: Vec<Client>,
|
clients: Vec<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "settings/providers_list.html")]
|
||||||
|
struct ProvidersListTemplate {
|
||||||
|
_p: BaseSettingsPage,
|
||||||
|
providers: Vec<Provider>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "settings/users_list.html")]
|
#[template(path = "settings/users_list.html")]
|
||||||
struct UsersListTemplate {
|
struct UsersListTemplate {
|
||||||
@@ -51,6 +59,20 @@ pub async fn clients_route(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn providers_route(
|
||||||
|
user: CurrentUser,
|
||||||
|
providers: web::Data<Arc<ProvidersManager>>,
|
||||||
|
) -> impl Responder {
|
||||||
|
HttpResponse::Ok().body(
|
||||||
|
ProvidersListTemplate {
|
||||||
|
_p: BaseSettingsPage::get("OpenID Providers list", &user, None, None),
|
||||||
|
providers: providers.cloned(),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug)]
|
#[derive(serde::Deserialize, Debug)]
|
||||||
pub struct UpdateUserQuery {
|
pub struct UpdateUserQuery {
|
||||||
uid: UserID,
|
uid: UserID,
|
||||||
@@ -62,6 +84,7 @@ pub struct UpdateUserQuery {
|
|||||||
enabled: Option<String>,
|
enabled: Option<String>,
|
||||||
two_factor_exemption_after_successful_login: Option<String>,
|
two_factor_exemption_after_successful_login: Option<String>,
|
||||||
admin: Option<String>,
|
admin: Option<String>,
|
||||||
|
allow_local_login: Option<String>,
|
||||||
grant_type: String,
|
grant_type: String,
|
||||||
granted_clients: String,
|
granted_clients: String,
|
||||||
two_factor: String,
|
two_factor: String,
|
||||||
@@ -136,6 +159,25 @@ pub async fn users_route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the list of authorized authentication sources
|
||||||
|
let auth_sources = AuthorizedAuthenticationSources {
|
||||||
|
local: update.0.allow_local_login.is_some(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if edited_user.authorized_authentication_sources() != auth_sources {
|
||||||
|
logger.log(Action::AdminSetAuthorizedAuthenticationSources(
|
||||||
|
&edited_user,
|
||||||
|
&auth_sources,
|
||||||
|
));
|
||||||
|
users
|
||||||
|
.send(users_actor::SetAuthorizedAuthenticationSources(
|
||||||
|
edited_user.uid.clone(),
|
||||||
|
auth_sources,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Update list of granted clients
|
// Update list of granted clients
|
||||||
let granted_clients = match update.0.grant_type.as_str() {
|
let granted_clients = match update.0.grant_type.as_str() {
|
||||||
"all_clients" => GrantedClients::AllClients,
|
"all_clients" => GrantedClients::AllClients,
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ pub async fn login_route(
|
|||||||
else if let Some(req) = &req {
|
else if let Some(req) = &req {
|
||||||
login = req.login.clone();
|
login = req.login.clone();
|
||||||
let response: LoginResult = users
|
let response: LoginResult = users
|
||||||
.send(users_actor::LoginRequest {
|
.send(users_actor::LocalLoginRequest {
|
||||||
login: login.clone(),
|
login: login.clone(),
|
||||||
password: req.password.clone(),
|
password: req.password.clone(),
|
||||||
})
|
})
|
||||||
@@ -163,6 +163,12 @@ pub async fn login_route(
|
|||||||
danger = Some("Your account is disabled!".to_string());
|
danger = Some("Your account is disabled!".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoginResult::LocalAuthForbidden => {
|
||||||
|
log::warn!("Failed login for username {} : attempted to use local auth, but it is forbidden", &login);
|
||||||
|
logger.log(Action::TryLocalLoginFromUnauthorizedAccount(&login));
|
||||||
|
danger = Some("You cannot login from local auth with your account!".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
LoginResult::Error => {
|
LoginResult::Error => {
|
||||||
danger = Some("An unkown error occured while trying to sign you in!".to_string());
|
danger = Some("An unkown error occured while trying to sign you in!".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use actix_web::dev::Payload;
|
|||||||
use actix_web::{web, Error, FromRequest, HttpRequest};
|
use actix_web::{web, Error, FromRequest, HttpRequest};
|
||||||
|
|
||||||
use crate::actors::users_actor;
|
use crate::actors::users_actor;
|
||||||
use crate::actors::users_actor::UsersActor;
|
use crate::actors::users_actor::{AuthorizedAuthenticationSources, UsersActor};
|
||||||
use crate::data::client::Client;
|
use crate::data::client::Client;
|
||||||
use crate::data::remote_ip::RemoteIP;
|
use crate::data::remote_ip::RemoteIP;
|
||||||
use crate::data::session_identity::SessionIdentity;
|
use crate::data::session_identity::SessionIdentity;
|
||||||
@@ -20,6 +20,7 @@ pub enum Action<'a> {
|
|||||||
AdminDeleteUser(&'a User),
|
AdminDeleteUser(&'a User),
|
||||||
AdminResetUserPassword(&'a User),
|
AdminResetUserPassword(&'a User),
|
||||||
AdminRemoveUserFactor(&'a User, &'a TwoFactor),
|
AdminRemoveUserFactor(&'a User, &'a TwoFactor),
|
||||||
|
AdminSetAuthorizedAuthenticationSources(&'a User, &'a AuthorizedAuthenticationSources),
|
||||||
AdminSetNewGrantedClientsList(&'a User, &'a GrantedClients),
|
AdminSetNewGrantedClientsList(&'a User, &'a GrantedClients),
|
||||||
AdminClear2FAHistory(&'a User),
|
AdminClear2FAHistory(&'a User),
|
||||||
LoginWebauthnAttempt { success: bool, user_id: UserID },
|
LoginWebauthnAttempt { success: bool, user_id: UserID },
|
||||||
@@ -28,6 +29,7 @@ pub enum Action<'a> {
|
|||||||
UserSuccessfullyAuthenticated(&'a User),
|
UserSuccessfullyAuthenticated(&'a User),
|
||||||
UserNeedNewPasswordOnLogin(&'a User),
|
UserNeedNewPasswordOnLogin(&'a User),
|
||||||
TryLoginWithDisabledAccount(&'a str),
|
TryLoginWithDisabledAccount(&'a str),
|
||||||
|
TryLocalLoginFromUnauthorizedAccount(&'a str),
|
||||||
FailedLoginWithBadCredentials(&'a str),
|
FailedLoginWithBadCredentials(&'a str),
|
||||||
UserChangedPasswordOnLogin(&'a UserID),
|
UserChangedPasswordOnLogin(&'a UserID),
|
||||||
OTPLoginAttempt { user: &'a User, success: bool },
|
OTPLoginAttempt { user: &'a User, success: bool },
|
||||||
@@ -64,6 +66,11 @@ impl<'a> Action<'a> {
|
|||||||
Action::AdminClear2FAHistory(user) => {
|
Action::AdminClear2FAHistory(user) => {
|
||||||
format!("cleared 2FA history of {}", user.quick_identity())
|
format!("cleared 2FA history of {}", user.quick_identity())
|
||||||
}
|
}
|
||||||
|
Action::AdminSetAuthorizedAuthenticationSources(user, sources) => format!(
|
||||||
|
"update authorized authentication sources ({:?}) for user ({})",
|
||||||
|
sources,
|
||||||
|
user.quick_identity()
|
||||||
|
),
|
||||||
Action::AdminSetNewGrantedClientsList(user, clients) => format!(
|
Action::AdminSetNewGrantedClientsList(user, clients) => format!(
|
||||||
"set new granted clients list ({:?}) for user ({})",
|
"set new granted clients list ({:?}) for user ({})",
|
||||||
clients,
|
clients,
|
||||||
@@ -90,6 +97,9 @@ impl<'a> Action<'a> {
|
|||||||
Action::TryLoginWithDisabledAccount(login) => {
|
Action::TryLoginWithDisabledAccount(login) => {
|
||||||
format!("successfully authenticated as {login}, but this is a DISABLED ACCOUNT")
|
format!("successfully authenticated as {login}, but this is a DISABLED ACCOUNT")
|
||||||
}
|
}
|
||||||
|
Action::TryLocalLoginFromUnauthorizedAccount(login) => {
|
||||||
|
format!("successfully locally authenticated as {login}, but this is a FORBIDDEN for this account!")
|
||||||
|
}
|
||||||
Action::FailedLoginWithBadCredentials(login) => {
|
Action::FailedLoginWithBadCredentials(login) => {
|
||||||
format!("attempted to authenticate as {login} but with a WRONG PASSWORD")
|
format!("attempted to authenticate as {login} but with a WRONG PASSWORD")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::constants::{APP_NAME, CLIENTS_LIST_FILE, USERS_LIST_FILE};
|
use crate::constants::{APP_NAME, CLIENTS_LIST_FILE, PROVIDERS_LIST_FILE, USERS_LIST_FILE};
|
||||||
|
|
||||||
/// Basic OIDC provider
|
/// Basic OIDC provider
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
@@ -72,6 +72,10 @@ impl AppConfig {
|
|||||||
self.storage_path().join(CLIENTS_LIST_FILE)
|
self.storage_path().join(CLIENTS_LIST_FILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn providers_file(&self) -> PathBuf {
|
||||||
|
self.storage_path().join(PROVIDERS_LIST_FILE)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn full_url(&self, uri: &str) -> String {
|
pub fn full_url(&self, uri: &str) -> String {
|
||||||
if uri.starts_with('/') {
|
if uri.starts_with('/') {
|
||||||
format!("{}{}", self.website_origin, uri)
|
format!("{}{}", self.website_origin, uri)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod jwt_signer;
|
|||||||
pub mod login_redirect;
|
pub mod login_redirect;
|
||||||
pub mod open_id_user_info;
|
pub mod open_id_user_info;
|
||||||
pub mod openid_config;
|
pub mod openid_config;
|
||||||
|
pub mod provider;
|
||||||
pub mod remote_ip;
|
pub mod remote_ip;
|
||||||
pub mod session_identity;
|
pub mod session_identity;
|
||||||
pub mod totp_key;
|
pub mod totp_key;
|
||||||
|
|||||||
74
src/data/provider.rs
Normal file
74
src/data/provider.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use crate::data::entity_manager::EntityManager;
|
||||||
|
use crate::utils::string_utils::apply_env_vars;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct ProviderID(pub String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Provider {
|
||||||
|
/// The ID of the provider
|
||||||
|
pub id: ProviderID,
|
||||||
|
|
||||||
|
/// The human-readable name of the client
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// A logo presented to the users of the provider
|
||||||
|
pub logo: String,
|
||||||
|
|
||||||
|
/// The registration id of BasicOIDC on the provider
|
||||||
|
pub client_id: String,
|
||||||
|
|
||||||
|
/// The registration secret of BasicOIDC on the provider
|
||||||
|
pub client_secret: String,
|
||||||
|
|
||||||
|
/// Specify the URL of the OpenID configuration URL
|
||||||
|
///
|
||||||
|
/// (.well-known/openid-configuration endpoint)
|
||||||
|
pub configuration_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Provider {
|
||||||
|
/// Get the URL where the logo can be located
|
||||||
|
pub fn logo_url(&self) -> &str {
|
||||||
|
match self.logo.as_str() {
|
||||||
|
"gitea" => "/assets/img/brands/gitea.svg",
|
||||||
|
"gitlab" => "/assets/img/brands/gitlab.svg",
|
||||||
|
"github" => "/assets/img/brands/github.svg",
|
||||||
|
"microsoft" => "/assets/img/brands/microsoft.svg",
|
||||||
|
"google" => "/assets/img/brands/google.svg",
|
||||||
|
s => s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Provider {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id.eq(&other.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Provider {}
|
||||||
|
|
||||||
|
pub type ProvidersManager = EntityManager<Provider>;
|
||||||
|
|
||||||
|
impl EntityManager<Provider> {
|
||||||
|
pub fn find_by_id(&self, u: &ProviderID) -> Option<Provider> {
|
||||||
|
for entry in self.iter() {
|
||||||
|
if entry.id.eq(u) {
|
||||||
|
return Some(entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_environment_variables(&mut self) {
|
||||||
|
for c in self.iter_mut() {
|
||||||
|
c.id = ProviderID(apply_env_vars(&c.id.0));
|
||||||
|
c.name = apply_env_vars(&c.name);
|
||||||
|
c.logo = apply_env_vars(&c.logo);
|
||||||
|
c.client_id = apply_env_vars(&c.client_id);
|
||||||
|
c.client_secret = apply_env_vars(&c.client_secret);
|
||||||
|
c.configuration_url = apply_env_vars(&c.configuration_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::actors::users_actor::AuthorizedAuthenticationSources;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
@@ -114,6 +115,10 @@ impl Successful2FALogin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub uid: UserID,
|
pub uid: UserID,
|
||||||
@@ -142,6 +147,10 @@ pub struct User {
|
|||||||
/// None = all services
|
/// None = all services
|
||||||
/// Some([]) = no service
|
/// Some([]) = no service
|
||||||
pub authorized_clients: Option<Vec<ClientID>>,
|
pub authorized_clients: Option<Vec<ClientID>>,
|
||||||
|
|
||||||
|
/// Authorize connection through local login
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub allow_local_login: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
@@ -162,6 +171,13 @@ impl User {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the list of sources from which a user can authenticate from
|
||||||
|
pub fn authorized_authentication_sources(&self) -> AuthorizedAuthenticationSources {
|
||||||
|
AuthorizedAuthenticationSources {
|
||||||
|
local: self.allow_local_login,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn granted_clients(&self) -> GrantedClients {
|
pub fn granted_clients(&self) -> GrantedClients {
|
||||||
match self.authorized_clients.as_deref() {
|
match self.authorized_clients.as_deref() {
|
||||||
None => GrantedClients::AllClients,
|
None => GrantedClients::AllClients,
|
||||||
@@ -296,6 +312,7 @@ impl Default for User {
|
|||||||
two_factor_exemption_after_successful_login: false,
|
two_factor_exemption_after_successful_login: false,
|
||||||
last_successful_2fa: Default::default(),
|
last_successful_2fa: Default::default(),
|
||||||
authorized_clients: Some(Vec::new()),
|
authorized_clients: Some(Vec::new()),
|
||||||
|
allow_local_login: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use crate::actors::users_actor::UsersSyncBackend;
|
use crate::actors::users_actor::{AuthorizedAuthenticationSources, UsersSyncBackend};
|
||||||
use crate::data::entity_manager::EntityManager;
|
use crate::data::entity_manager::EntityManager;
|
||||||
use crate::data::user::{FactorID, GeneralSettings, GrantedClients, TwoFactor, User, UserID};
|
use crate::data::user::{FactorID, GeneralSettings, GrantedClients, TwoFactor, User, UserID};
|
||||||
use crate::utils::err::{new_error, Res};
|
use crate::utils::err::{new_error, Res};
|
||||||
@@ -143,6 +143,17 @@ impl UsersSyncBackend for EntityManager<User> {
|
|||||||
self.remove(&user)
|
self.remove(&user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_authorized_authentication_sources(
|
||||||
|
&mut self,
|
||||||
|
id: &UserID,
|
||||||
|
sources: AuthorizedAuthenticationSources,
|
||||||
|
) -> Res {
|
||||||
|
self.update_user(id, |mut user| {
|
||||||
|
user.allow_local_login = sources.local;
|
||||||
|
user
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res {
|
fn set_granted_2fa_clients(&mut self, id: &UserID, clients: GrantedClients) -> Res {
|
||||||
self.update_user(id, |mut user| {
|
self.update_user(id, |mut user| {
|
||||||
user.authorized_clients = clients.to_user();
|
user.authorized_clients = clients.to_user();
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -20,6 +20,7 @@ use basic_oidc::data::app_config::AppConfig;
|
|||||||
use basic_oidc::data::client::ClientManager;
|
use basic_oidc::data::client::ClientManager;
|
||||||
use basic_oidc::data::entity_manager::EntityManager;
|
use basic_oidc::data::entity_manager::EntityManager;
|
||||||
use basic_oidc::data::jwt_signer::JWTSigner;
|
use basic_oidc::data::jwt_signer::JWTSigner;
|
||||||
|
use basic_oidc::data::provider::ProvidersManager;
|
||||||
use basic_oidc::data::user::User;
|
use basic_oidc::data::user::User;
|
||||||
use basic_oidc::data::webauthn_manager::WebAuthManager;
|
use basic_oidc::data::webauthn_manager::WebAuthManager;
|
||||||
use basic_oidc::middlewares::auth_middleware::AuthMiddleware;
|
use basic_oidc::middlewares::auth_middleware::AuthMiddleware;
|
||||||
@@ -77,6 +78,11 @@ async fn main() -> std::io::Result<()> {
|
|||||||
clients.apply_environment_variables();
|
clients.apply_environment_variables();
|
||||||
let clients = Arc::new(clients);
|
let clients = Arc::new(clients);
|
||||||
|
|
||||||
|
let mut providers = ProvidersManager::open_or_create(config.providers_file())
|
||||||
|
.expect("Failed to load providers list!");
|
||||||
|
providers.apply_environment_variables();
|
||||||
|
let providers = Arc::new(providers);
|
||||||
|
|
||||||
log::info!("Server will listen on {}", config.listen_address);
|
log::info!("Server will listen on {}", config.listen_address);
|
||||||
let listen_address = config.listen_address.to_string();
|
let listen_address = config.listen_address.to_string();
|
||||||
|
|
||||||
@@ -101,6 +107,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.app_data(web::Data::new(bruteforce_actor.clone()))
|
.app_data(web::Data::new(bruteforce_actor.clone()))
|
||||||
.app_data(web::Data::new(openid_sessions_actor.clone()))
|
.app_data(web::Data::new(openid_sessions_actor.clone()))
|
||||||
.app_data(web::Data::new(clients.clone()))
|
.app_data(web::Data::new(clients.clone()))
|
||||||
|
.app_data(web::Data::new(providers.clone()))
|
||||||
.app_data(web::Data::new(jwt_signer.clone()))
|
.app_data(web::Data::new(jwt_signer.clone()))
|
||||||
.app_data(web::Data::new(webauthn_manager.clone()))
|
.app_data(web::Data::new(webauthn_manager.clone()))
|
||||||
.wrap(
|
.wrap(
|
||||||
@@ -207,6 +214,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/admin/clients",
|
"/admin/clients",
|
||||||
web::get().to(admin_controller::clients_route),
|
web::get().to(admin_controller::clients_route),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/admin/providers",
|
||||||
|
web::get().to(admin_controller::providers_route),
|
||||||
|
)
|
||||||
.route("/admin/users", web::get().to(admin_controller::users_route))
|
.route("/admin/users", web::get().to(admin_controller::users_route))
|
||||||
.route(
|
.route(
|
||||||
"/admin/users",
|
"/admin/users",
|
||||||
|
|||||||
@@ -42,6 +42,11 @@
|
|||||||
Clients
|
Clients
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/providers" class="nav-link link-dark">
|
||||||
|
Providers
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/users" class="nav-link link-dark">
|
<a href="/admin/users" class="nav-link link-dark">
|
||||||
Users
|
Users
|
||||||
@@ -83,6 +88,7 @@
|
|||||||
if(el.href === location.href) el.classList.add("active");
|
if(el.href === location.href) el.classList.add("active");
|
||||||
else el.classList.remove("active")
|
else el.classList.remove("active")
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% if _p.ip_location_api.is_some() %}
|
{% if _p.ip_location_api.is_some() %}
|
||||||
<script>const IP_LOCATION_API = "{{ _p.ip_location_api.unwrap() }}"</script>
|
<script>const IP_LOCATION_API = "{{ _p.ip_location_api.unwrap() }}"</script>
|
||||||
|
|||||||
@@ -113,27 +113,44 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{% for e in u.get_formatted_2fa_successful_logins() %}
|
{% for e in u.get_formatted_2fa_successful_logins() %}
|
||||||
{% if e.can_bypass_2fa %}<li style="font-weight: bold;">{{ e.ip }} - {{ e.fmt_time() }} - BYPASS 2FA</li>
|
{% if e.can_bypass_2fa %}
|
||||||
{% else %}<li>{{ e.ip }} - {{ e.fmt_time() }}</li>{% endif %}
|
<li style="font-weight: bold;">{{ e.ip }} - {{ e.fmt_time() }} - BYPASS 2FA</li>
|
||||||
|
{% else %}
|
||||||
|
<li>{{ e.ip }} - {{ e.fmt_time() }}</li>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Authorized authentication sources -->
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<legend class="mt-4">Authorized authentication sources</legend>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="allow_local_login" id="allow_local_login"
|
||||||
|
{% if u.allow_local_login %} checked="" {% endif %}>
|
||||||
|
<label class="form-check-label" for="allow_local_login">
|
||||||
|
Allow local login
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- Granted clients -->
|
<!-- Granted clients -->
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<legend class="mt-4">Granted clients</legend>
|
<legend class="mt-4">Granted clients</legend>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input type="radio" class="form-check-input" name="grant_type"
|
<input type="radio" class="form-check-input" name="grant_type"
|
||||||
value="all_clients" {% if u.granted_clients() == GrantedClients::AllClients %} checked="" {% endif %}>
|
value="all_clients" {% if u.granted_clients()== GrantedClients::AllClients %} checked="" {% endif
|
||||||
|
%}>
|
||||||
Grant all clients
|
Grant all clients
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input type="radio" class="form-check-input" name="grant_type"
|
<input type="radio" class="form-check-input" name="grant_type"
|
||||||
value="custom_clients" {% if matches!(self.u.granted_clients(), GrantedClients::SomeClients(_)) %} checked="checked" {% endif %}>
|
value="custom_clients" {% if matches!(self.u.granted_clients(), GrantedClients::SomeClients(_))
|
||||||
|
%} checked="checked" {% endif %}>
|
||||||
Manually specify allowed clients
|
Manually specify allowed clients
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,7 +172,8 @@
|
|||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label class="form-check-label">
|
<label class="form-check-label">
|
||||||
<input type="radio" class="form-check-input" name="grant_type"
|
<input type="radio" class="form-check-input" name="grant_type"
|
||||||
value="no_client" {% if u.granted_clients() == GrantedClients::NoClient %} checked="checked" {% endif %}>
|
value="no_client" {% if u.granted_clients()== GrantedClients::NoClient %} checked="checked" {%
|
||||||
|
endif %}>
|
||||||
Do not grant any client
|
Do not grant any client
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -231,6 +249,7 @@
|
|||||||
form.submit();
|
form.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
37
templates/settings/providers_list.html
Normal file
37
templates/settings/providers_list.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{% extends "base_settings_page.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#providers td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<table id="providers" class="table table-hover" style="max-width: 800px;" aria-describedby="OpenID providers list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Configuration URL</th>
|
||||||
|
<th scope="col">Client ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for c in providers %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="{{ c.logo_url() }}" alt="{{ c.name }} logo" width="30px"/>
|
||||||
|
</td>
|
||||||
|
<td>{{ c.id.0 }}</td>
|
||||||
|
<td>{{ c.name }}</td>
|
||||||
|
<td><a href="{{ c.configuration_url }}" target="_blank" rel="noreferrer">
|
||||||
|
{{ c.configuration_url }}
|
||||||
|
</a></td>
|
||||||
|
<td>{{ c.client_id }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
Reference in New Issue
Block a user