actix-web-httpauth-0.8.2/.cargo_vcs_info.json0000644000000001600000000000100145620ustar { "git": { "sha1": "8aa2c959c445f36285e284f459c24850b5defd78" }, "path_in_vcs": "actix-web-httpauth" }actix-web-httpauth-0.8.2/CHANGES.md000064400000000000000000000074071046102023000147570ustar 00000000000000# Changes ## Unreleased ## 0.8.2 - Minimum supported Rust version (MSRV) is now 1.75. ## 0.8.1 - Implement `From` for `BasicAuth`. - Minimum supported Rust version (MSRV) is now 1.68. ## 0.8.0 - Removed `AuthExtractor` trait; implement `FromRequest` for your custom auth types. [#264] - `BasicAuth::user_id()` now returns `&str`. [#249] - `BasicAuth::password()` now returns `Option<&str>`. [#249] - `Basic::user_id()` now returns `&str`. [#264] - `Basic::password()` now returns `Option<&str>`. [#264] - `Bearer::token()` now returns `&str`. [#264] [#249]: https://github.com/actix/actix-extras/pull/249 [#264]: https://github.com/actix/actix-extras/pull/264 ## 0.7.0 - Auth validator functions now need to return `(Error, ServiceRequest)` in error cases. [#260] - Minimum supported Rust version (MSRV) is now 1.57 due to transitive `time` dependency. [#260]: https://github.com/actix/actix-extras/pull/260 ## 0.6.0 - Update `actix-web` dependency to `4`. ## 0.6.0-beta.8 - Relax body type bounds on middleware impl. [#223] - Update `actix-web` dependency to `4.0.0-rc.1`. [#223]: https://github.com/actix/actix-extras/pull/223 ## 0.6.0-beta.7 - Minimum supported Rust version (MSRV) is now 1.54. ## 0.6.0-beta.6 - Update `actix-web` dependency to `4.0.0.beta-15`. [#216] [#216]: https://github.com/actix/actix-extras/pull/216 ## 0.6.0-beta.5 - Update `actix-web` dependency to `4.0.0.beta-14`. [#209] [#209]: https://github.com/actix/actix-extras/pull/209 ## 0.6.0-beta.4 - impl `AuthExtractor` trait for `Option` and `Result`. [#205] [#205]: https://github.com/actix/actix-extras/pull/205 ## 0.6.0-beta.3 - Update `actix-web` dependency to v4.0.0-beta.10. [#203] - Minimum supported Rust version (MSRV) is now 1.52. [#203]: https://github.com/actix/actix-extras/pull/203 ## 0.6.0-beta.2 - No notable changes. ## 0.6.0-beta.1 - Update `actix-web` dependency to 4.0.0 beta. - Minimum supported Rust version (MSRV) is now 1.46.0. ## 0.5.1 - Correct error handling when extracting auth details from request. [#128] [#128]: https://github.com/actix/actix-extras/pull/128 ## 0.5.0 - Update `actix-web` dependency to 3.0.0. - Minimum supported Rust version (MSRV) is now 1.42.0. ## 0.4.2 - Update the `base64` dependency to 0.12 - AuthenticationError's status code is preserved when converting to a ResponseError - Minimize `futures` dependency - Fix panic on `AuthenticationMiddleware` [#69] [#69]: https://github.com/actix/actix-web-httpauth/pull/69 ## 0.4.1 - Move repository to actix-extras ## 0.4.0 - Depends on `actix-web = "^2.0"`, `actix-service = "^1.0"`, and `futures = "^0.3"` version now ([#14]) - Depends on `bytes = "^0.5"` and `base64 = "^0.11"` now [#14]: https://github.com/actix/actix-web-httpauth/pull/14 ## 0.3.2 - 2019-07-19 - Middleware accepts any `Fn` as a validator function instead of `FnMut` [#11] [#11]: https://github.com/actix/actix-web-httpauth/pull/11 ## 0.3.1 - 2019-06-09 - Multiple calls to the middleware would result in panic ## 0.3.0 - 2019-06-05 - Crate edition was changed to `2018`, same as `actix-web` - Depends on `actix-web = "^1.0"` version now - `WWWAuthenticate` header struct was renamed into `WwwAuthenticate` - Challenges and extractor configs are now operating with `Cow<'static, str>` types instead of `String` types ## 0.2.0 - 2019-04-26 - `actix-web` dependency is used without default features now [#6] - `base64` dependency version was bumped to `0.10` [#6]: https://github.com/actix/actix-web-httpauth/pull/6 ## 0.1.0 - 2018-09-08 - Update to `actix-web = "0.7"` version ## 0.0.4 - 2018-07-01 - Fix possible panic at `IntoHeaderValue` implementation for `headers::authorization::Basic` - Fix possible panic at `headers::www_authenticate::challenge::bearer::Bearer::to_bytes` call actix-web-httpauth-0.8.2/Cargo.lock0000644000001156100000000000100125440ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "actix-codec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ "bitflags", "bytes", "futures-core", "futures-sink", "memchr", "pin-project-lite", "tokio", "tokio-util", "tracing", ] [[package]] name = "actix-cors" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331" dependencies = [ "actix-utils", "actix-web", "derive_more", "futures-util", "log", "once_cell", "smallvec", ] [[package]] name = "actix-http" version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", "ahash", "base64", "bitflags", "bytes", "bytestring", "derive_more", "encoding_rs", "futures-core", "http", "httparse", "httpdate", "itoa", "language-tags", "local-channel", "mime", "percent-encoding", "pin-project-lite", "rand", "sha1", "smallvec", "tokio", "tokio-util", "tracing", ] [[package]] name = "actix-macros" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", "syn 2.0.66", ] [[package]] name = "actix-router" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", "cfg-if", "http", "regex-lite", "serde", "tracing", ] [[package]] name = "actix-rt" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "futures-core", "tokio", ] [[package]] name = "actix-server" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5" dependencies = [ "actix-rt", "actix-service", "actix-utils", "futures-core", "futures-util", "mio", "socket2", "tokio", "tracing", ] [[package]] name = "actix-service" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" dependencies = [ "futures-core", "paste", "pin-project-lite", ] [[package]] name = "actix-utils" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" dependencies = [ "local-waker", "pin-project-lite", ] [[package]] name = "actix-web" version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6316df3fa569627c98b12557a8b6ff0674e5be4bb9b5e4ae2550ddb4964ed6" dependencies = [ "actix-codec", "actix-http", "actix-macros", "actix-router", "actix-rt", "actix-server", "actix-service", "actix-utils", "actix-web-codegen", "ahash", "bytes", "bytestring", "cfg-if", "derive_more", "encoding_rs", "futures-core", "futures-util", "itoa", "language-tags", "log", "mime", "once_cell", "pin-project-lite", "regex-lite", "serde", "serde_json", "serde_urlencoded", "smallvec", "socket2", "time", "url", ] [[package]] name = "actix-web-codegen" version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" dependencies = [ "actix-router", "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "actix-web-httpauth" version = "0.8.2" dependencies = [ "actix-cors", "actix-service", "actix-utils", "actix-web", "base64", "futures-core", "futures-util", "log", "pin-project-lite", "tracing", "tracing-subscriber", ] [[package]] name = "addr2line" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bytestring" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" dependencies = [ "bytes", ] [[package]] name = "cc" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cpufeatures" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", "syn 1.0.109", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "displaydoc" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "encoding_rs" version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "http" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "httparse" version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "idna" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ "icu_normalizer", "icu_properties", "smallvec", "utf8_iter", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "language-tags" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "litemap" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "local-channel" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" dependencies = [ "futures-core", "futures-sink", "local-waker", ] [[package]] name = "local-waker" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", "wasi", "windows-sys 0.48.0", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "object" version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.52.5", ] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.4", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.4", ] [[package]] name = "regex-lite" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tokio" version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "windows-sys 0.48.0", ] [[package]] name = "tokio-util" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "url" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[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]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yoke" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", "synstructure", ] [[package]] name = "zerocopy" version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "zerofrom" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", "synstructure", ] [[package]] name = "zerovec" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] actix-web-httpauth-0.8.2/Cargo.toml0000644000000033400000000000100125630ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.75" name = "actix-web-httpauth" version = "0.8.2" authors = [ "svartalf ", "Yuki Okushi ", ] description = "HTTP authentication schemes for Actix Web" homepage = "https://actix.rs" readme = "README.md" keywords = [ "http", "web", "framework", "authentication", "security", ] categories = ["web-programming"] license = "MIT OR Apache-2.0" repository = "https://github.com/actix/actix-extras" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.actix-utils] version = "3" [dependencies.actix-web] version = "4.1" default-features = false [dependencies.base64] version = "0.22" [dependencies.futures-core] version = "0.3.17" [dependencies.futures-util] version = "0.3.17" features = ["std"] default-features = false [dependencies.log] version = "0.4" [dependencies.pin-project-lite] version = "0.2.7" [dev-dependencies.actix-cors] version = "0.7" [dev-dependencies.actix-service] version = "2" [dev-dependencies.actix-web] version = "4.1" features = ["macros"] default-features = false [dev-dependencies.tracing] version = "0.1.30" [dev-dependencies.tracing-subscriber] version = "0.3" features = ["env-filter"] actix-web-httpauth-0.8.2/Cargo.toml.orig000064400000000000000000000017631046102023000162530ustar 00000000000000[package] name = "actix-web-httpauth" version = "0.8.2" description = "HTTP authentication schemes for Actix Web" categories = ["web-programming"] keywords = ["http", "web", "framework", "authentication", "security"] authors = [ "svartalf ", "Yuki Okushi ", ] homepage.workspace = true repository.workspace = true license.workspace = true edition.workspace = true rust-version.workspace = true [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] all-features = true [dependencies] actix-utils = "3" actix-web = { version = "4.1", default-features = false } base64 = "0.22" futures-core = "0.3.17" futures-util = { version = "0.3.17", default-features = false, features = ["std"] } log = "0.4" pin-project-lite = "0.2.7" [dev-dependencies] actix-cors = "0.7" actix-service = "2" actix-web = { version = "4.1", default-features = false, features = ["macros"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing = "0.1.30" actix-web-httpauth-0.8.2/LICENSE-APACHE000064400000000000000000000261201046102023000153020ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017-NOW Actix team Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. actix-web-httpauth-0.8.2/LICENSE-MIT000064400000000000000000000020361046102023000150120ustar 00000000000000Copyright (c) 2023 Actix team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. actix-web-httpauth-0.8.2/README.md000064400000000000000000000027131046102023000146370ustar 00000000000000# actix-web-httpauth > HTTP authentication schemes for [Actix Web](https://actix.rs). [![crates.io](https://img.shields.io/crates/v/actix-web-httpauth?label=latest)](https://crates.io/crates/actix-web-httpauth) [![Documentation](https://docs.rs/actix-web-httpauth/badge.svg?version=0.8.2)](https://docs.rs/actix-web-httpauth/0.8.2) ![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-web-httpauth) [![Dependency Status](https://deps.rs/crate/actix-web-httpauth/0.8.2/status.svg)](https://deps.rs/crate/actix-web-httpauth/0.8.2) ## Documentation & Resources - [API Documentation](https://docs.rs/actix-web-httpauth/) - Minimum Supported Rust Version (MSRV): 1.57 ## Features - Typed [Authorization] and [WWW-Authenticate] headers - [Extractors] for authorization headers - [Middleware] for easier authorization checking All supported schemas can be used in both middleware and request handlers. ## Supported Schemes - [HTTP Basic](https://tools.ietf.org/html/rfc7617) - [OAuth Bearer](https://tools.ietf.org/html/rfc6750) [Authorization]: https://docs.rs/actix-web-httpauth/*/actix_web_httpauth/headers/authorization/index.html [WWW-Authenticate]: https://docs.rs/actix-web-httpauth/*/actix_web_httpauth/headers/www_authenticate/index.html [Extractors]: https://actix.rs/docs/extractors/ [Middleware]: https://docs.rs/actix-web-httpauth/*/actix_web_httpauth/middleware/index.html actix-web-httpauth-0.8.2/src/extractors/basic.rs000064400000000000000000000063051046102023000177750ustar 00000000000000//! Extractor for the "Basic" HTTP Authentication Scheme. use std::borrow::Cow; use actix_utils::future::{ready, Ready}; use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest}; use super::{config::AuthExtractorConfig, errors::AuthenticationError}; use crate::headers::{ authorization::{Authorization, Basic}, www_authenticate::basic::Basic as Challenge, }; /// [`BasicAuth`] extractor configuration used for [`WWW-Authenticate`] header later. /// /// [`WWW-Authenticate`]: crate::headers::www_authenticate::WwwAuthenticate #[derive(Debug, Clone, Default)] pub struct Config(Challenge); impl Config { /// Set challenge `realm` attribute. /// /// The "realm" attribute indicates the scope of protection in the manner described in HTTP/1.1 /// [RFC 2617 §1.2](https://tools.ietf.org/html/rfc2617#section-1.2). pub fn realm(mut self, value: T) -> Config where T: Into>, { self.0.realm = Some(value.into()); self } } impl AsRef for Config { fn as_ref(&self) -> &Challenge { &self.0 } } impl AuthExtractorConfig for Config { type Inner = Challenge; fn into_inner(self) -> Self::Inner { self.0 } } /// Extractor for HTTP Basic auth. /// /// # Examples /// ``` /// use actix_web_httpauth::extractors::basic::BasicAuth; /// /// async fn index(auth: BasicAuth) -> String { /// format!("Hello, {}!", auth.user_id()) /// } /// ``` /// /// If authentication fails, this extractor fetches the [`Config`] instance from the [app data] in /// order to properly form the `WWW-Authenticate` response header. /// /// # Examples /// ``` /// use actix_web::{web, App}; /// use actix_web_httpauth::extractors::basic::{self, BasicAuth}; /// /// async fn index(auth: BasicAuth) -> String { /// format!("Hello, {}!", auth.user_id()) /// } /// /// App::new() /// .app_data(basic::Config::default().realm("Restricted area")) /// .service(web::resource("/index.html").route(web::get().to(index))); /// ``` /// /// [app data]: https://docs.rs/actix-web/4/actix_web/struct.App.html#method.app_data #[derive(Debug, Clone)] pub struct BasicAuth(Basic); impl BasicAuth { /// Returns client's user-ID. pub fn user_id(&self) -> &str { self.0.user_id() } /// Returns client's password. pub fn password(&self) -> Option<&str> { self.0.password() } } impl From for BasicAuth { fn from(basic: Basic) -> Self { Self(basic) } } impl FromRequest for BasicAuth { type Future = Ready>; type Error = AuthenticationError; fn from_request(req: &HttpRequest, _: &mut Payload) -> ::Future { ready( Authorization::::parse(req) .map(|auth| BasicAuth(auth.into_scheme())) .map_err(|err| { log::debug!("`BasicAuth` extract error: {}", err); let challenge = req .app_data::() .map(|config| config.0.clone()) .unwrap_or_default(); AuthenticationError::new(challenge) }), ) } } actix-web-httpauth-0.8.2/src/extractors/bearer.rs000064400000000000000000000104321046102023000201500ustar 00000000000000//! Extractor for the "Bearer" HTTP Authentication Scheme. use std::{borrow::Cow, default::Default}; use actix_utils::future::{ready, Ready}; use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest}; use super::{config::AuthExtractorConfig, errors::AuthenticationError}; pub use crate::headers::www_authenticate::bearer::Error; use crate::headers::{authorization, www_authenticate::bearer}; /// [`BearerAuth`] extractor configuration. #[derive(Debug, Clone, Default)] pub struct Config(bearer::Bearer); impl Config { /// Set challenge `scope` attribute. /// /// The `"scope"` attribute is a space-delimited list of case-sensitive /// scope values indicating the required scope of the access token for /// accessing the requested resource. pub fn scope>>(mut self, value: T) -> Config { self.0.scope = Some(value.into()); self } /// Set challenge `realm` attribute. /// /// The "realm" attribute indicates the scope of protection in the manner /// described in HTTP/1.1 [RFC 2617](https://tools.ietf.org/html/rfc2617#section-1.2). pub fn realm>>(mut self, value: T) -> Config { self.0.realm = Some(value.into()); self } } impl AsRef for Config { fn as_ref(&self) -> &bearer::Bearer { &self.0 } } impl AuthExtractorConfig for Config { type Inner = bearer::Bearer; fn into_inner(self) -> Self::Inner { self.0 } } /// Extractor for HTTP Bearer auth /// /// # Examples /// ``` /// use actix_web_httpauth::extractors::bearer::BearerAuth; /// /// async fn index(auth: BearerAuth) -> String { /// format!("Hello, user with token {}!", auth.token()) /// } /// ``` /// /// If authentication fails, this extractor fetches the [`Config`] instance /// from the [app data] in order to properly form the `WWW-Authenticate` /// response header. /// /// # Examples /// ``` /// use actix_web::{web, App}; /// use actix_web_httpauth::extractors::bearer::{self, BearerAuth}; /// /// async fn index(auth: BearerAuth) -> String { /// format!("Hello, {}!", auth.token()) /// } /// /// App::new() /// .app_data( /// bearer::Config::default() /// .realm("Restricted area") /// .scope("email photo"), /// ) /// .service(web::resource("/index.html").route(web::get().to(index))); /// ``` #[derive(Debug, Clone)] pub struct BearerAuth(authorization::Bearer); impl BearerAuth { /// Returns bearer token provided by client. pub fn token(&self) -> &str { self.0.token() } } impl FromRequest for BearerAuth { type Future = Ready>; type Error = AuthenticationError; fn from_request(req: &HttpRequest, _payload: &mut Payload) -> ::Future { ready( authorization::Authorization::::parse(req) .map(|auth| BearerAuth(auth.into_scheme())) .map_err(|_| { let bearer = req .app_data::() .map(|config| config.0.clone()) .unwrap_or_default(); AuthenticationError::new(bearer) }), ) } } /// Extended error customization for HTTP `Bearer` auth. impl AuthenticationError { /// Attach `Error` to the current Authentication error. /// /// Error status code will be changed to the one provided by the `kind` /// Error. pub fn with_error(mut self, kind: Error) -> Self { *self.status_code_mut() = kind.status_code(); self.challenge_mut().error = Some(kind); self } /// Attach error description to the current Authentication error. pub fn with_error_description(mut self, desc: T) -> Self where T: Into>, { self.challenge_mut().error_description = Some(desc.into()); self } /// Attach error URI to the current Authentication error. /// /// It is up to implementor to provide properly formed absolute URI. pub fn with_error_uri(mut self, uri: T) -> Self where T: Into>, { self.challenge_mut().error_uri = Some(uri.into()); self } } actix-web-httpauth-0.8.2/src/extractors/config.rs000064400000000000000000000011351046102023000201550ustar 00000000000000use super::AuthenticationError; use crate::headers::www_authenticate::Challenge; /// Trait implemented for types that provides configuration for the authentication /// [extractors](crate::extractors). pub trait AuthExtractorConfig { /// Associated challenge type. type Inner: Challenge; /// Convert the config instance into a HTTP challenge. fn into_inner(self) -> Self::Inner; } impl From for AuthenticationError<::Inner> where T: AuthExtractorConfig, { fn from(config: T) -> Self { AuthenticationError::new(config.into_inner()) } } actix-web-httpauth-0.8.2/src/extractors/errors.rs000064400000000000000000000046231046102023000202310ustar 00000000000000use std::{error::Error, fmt}; use actix_web::{http::StatusCode, HttpResponse, ResponseError}; use crate::headers::www_authenticate::{Challenge, WwwAuthenticate}; /// Authentication error returned by authentication extractors. /// /// Different extractors may extend `AuthenticationError` implementation in order to provide access /// inner challenge fields. #[derive(Debug)] pub struct AuthenticationError { challenge: C, status_code: StatusCode, } impl AuthenticationError { /// Creates new authentication error from the provided `challenge`. /// /// By default returned error will resolve into the `HTTP 401` status code. pub fn new(challenge: C) -> AuthenticationError { AuthenticationError { challenge, status_code: StatusCode::UNAUTHORIZED, } } /// Returns mutable reference to the inner challenge instance. pub fn challenge_mut(&mut self) -> &mut C { &mut self.challenge } /// Returns mutable reference to the inner status code. /// /// Can be used to override returned status code, but by default this lib tries to stick to the /// RFC, so it might be unreasonable. pub fn status_code_mut(&mut self) -> &mut StatusCode { &mut self.status_code } } impl fmt::Display for AuthenticationError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.status_code, f) } } impl Error for AuthenticationError {} impl ResponseError for AuthenticationError { fn status_code(&self) -> StatusCode { self.status_code } fn error_response(&self) -> HttpResponse { HttpResponse::build(self.status_code()) .insert_header(WwwAuthenticate(self.challenge.clone())) .finish() } } #[cfg(test)] mod tests { use actix_web::Error; use super::*; use crate::headers::www_authenticate::basic::Basic; #[test] fn test_status_code_is_preserved_across_error_conversions() { let ae = AuthenticationError::new(Basic::default()); let expected = ae.status_code; // Converting the AuthenticationError into a ResponseError should preserve the status code. let err = Error::from(ae); let res_err = err.as_response_error(); assert_eq!(expected, res_err.status_code()); } } actix-web-httpauth-0.8.2/src/extractors/mod.rs000064400000000000000000000002701046102023000174660ustar 00000000000000//! Type-safe authentication information extractors. pub mod basic; pub mod bearer; mod config; mod errors; pub use self::{config::AuthExtractorConfig, errors::AuthenticationError}; actix-web-httpauth-0.8.2/src/headers/authorization/errors.rs000064400000000000000000000041501046102023000223410ustar 00000000000000use std::{error::Error, fmt, str}; use actix_web::http::header; /// Possible errors while parsing `Authorization` header. /// /// Should not be used directly unless you are implementing your own /// [authentication scheme](super::Scheme). #[derive(Debug)] pub enum ParseError { /// Header value is malformed. Invalid, /// Authentication scheme is missing. MissingScheme, /// Required authentication field is missing. MissingField(&'static str), /// Unable to convert header into the str. ToStrError(header::ToStrError), /// Malformed base64 string. Base64DecodeError(base64::DecodeError), /// Malformed UTF-8 string. Utf8Error(str::Utf8Error), } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseError::Invalid => f.write_str("Invalid header value"), ParseError::MissingScheme => f.write_str("Missing authorization scheme"), ParseError::MissingField(field) => write!(f, "Missing header field ({field})"), ParseError::ToStrError(err) => fmt::Display::fmt(err, f), ParseError::Base64DecodeError(err) => fmt::Display::fmt(err, f), ParseError::Utf8Error(err) => fmt::Display::fmt(err, f), } } } impl Error for ParseError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ParseError::Invalid => None, ParseError::MissingScheme => None, ParseError::MissingField(_) => None, ParseError::ToStrError(err) => Some(err), ParseError::Base64DecodeError(err) => Some(err), ParseError::Utf8Error(err) => Some(err), } } } impl From for ParseError { fn from(err: header::ToStrError) -> Self { ParseError::ToStrError(err) } } impl From for ParseError { fn from(err: base64::DecodeError) -> Self { ParseError::Base64DecodeError(err) } } impl From for ParseError { fn from(err: str::Utf8Error) -> Self { ParseError::Utf8Error(err) } } actix-web-httpauth-0.8.2/src/headers/authorization/header.rs000064400000000000000000000046701046102023000222640ustar 00000000000000use std::fmt; use actix_web::{ error::ParseError, http::header::{Header, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION}, HttpMessage, }; use crate::headers::authorization::scheme::Scheme; /// `Authorization` header, defined in [RFC 7235](https://tools.ietf.org/html/rfc7235#section-4.2) /// /// The "Authorization" header field allows a user agent to authenticate itself with an origin /// server—usually, but not necessarily, after receiving a 401 (Unauthorized) response. Its value /// consists of credentials containing the authentication information of the user agent for the /// realm of the resource being requested. /// /// `Authorization` is generic over an [authentication scheme](Scheme). /// /// # Examples /// ``` /// # use actix_web::{HttpRequest, Result, http::header::Header}; /// # use actix_web_httpauth::headers::authorization::{Authorization, Basic}; /// fn handler(req: HttpRequest) -> Result { /// let auth = Authorization::::parse(&req)?; /// /// Ok(format!("Hello, {}!", auth.as_ref().user_id())) /// } /// ``` #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Authorization(S); impl Authorization { /// Consumes `Authorization` header and returns inner [`Scheme`] implementation. pub fn into_scheme(self) -> S { self.0 } } impl From for Authorization { fn from(scheme: S) -> Authorization { Authorization(scheme) } } impl AsRef for Authorization { fn as_ref(&self) -> &S { &self.0 } } impl AsMut for Authorization { fn as_mut(&mut self) -> &mut S { &mut self.0 } } impl fmt::Display for Authorization { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl Header for Authorization { #[inline] fn name() -> HeaderName { AUTHORIZATION } fn parse(msg: &T) -> Result { let header = msg.headers().get(Self::name()).ok_or(ParseError::Header)?; let scheme = S::parse(header).map_err(|_| ParseError::Header)?; Ok(Authorization(scheme)) } } impl TryIntoHeaderValue for Authorization { type Error = ::Error; fn try_into_value(self) -> Result { self.0.try_into_value() } } actix-web-httpauth-0.8.2/src/headers/authorization/mod.rs000064400000000000000000000003251046102023000216040ustar 00000000000000//! `Authorization` header and various auth schemes. mod errors; mod header; mod scheme; pub use self::{ errors::ParseError, header::Authorization, scheme::{basic::Basic, bearer::Bearer, Scheme}, }; actix-web-httpauth-0.8.2/src/headers/authorization/scheme/basic.rs000064400000000000000000000130561046102023000233570ustar 00000000000000use std::{borrow::Cow, fmt, str}; use actix_web::{ http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, web::{BufMut, BytesMut}, }; use base64::{prelude::BASE64_STANDARD, Engine}; use crate::headers::authorization::{errors::ParseError, Scheme}; /// Credentials for `Basic` authentication scheme, defined in [RFC 7617](https://tools.ietf.org/html/rfc7617) #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Basic { user_id: Cow<'static, str>, password: Option>, } impl Basic { /// Creates `Basic` credentials with provided `user_id` and optional /// `password`. /// /// # Examples /// ``` /// # use actix_web_httpauth::headers::authorization::Basic; /// let credentials = Basic::new("Alladin", Some("open sesame")); /// ``` pub fn new(user_id: U, password: Option

) -> Basic where U: Into>, P: Into>, { Basic { user_id: user_id.into(), password: password.map(Into::into), } } /// Returns client's user-ID. pub fn user_id(&self) -> &str { self.user_id.as_ref() } /// Returns client's password if provided. pub fn password(&self) -> Option<&str> { self.password.as_deref() } } impl Scheme for Basic { fn parse(header: &HeaderValue) -> Result { // "Basic *" length if header.len() < 7 { return Err(ParseError::Invalid); } let mut parts = header.to_str()?.splitn(2, ' '); match parts.next() { Some("Basic") => (), _ => return Err(ParseError::MissingScheme), } let decoded = BASE64_STANDARD.decode(parts.next().ok_or(ParseError::Invalid)?)?; let mut credentials = str::from_utf8(&decoded)?.splitn(2, ':'); let user_id = credentials .next() .ok_or(ParseError::MissingField("user_id")) .map(|user_id| user_id.to_string().into())?; let password = credentials .next() .ok_or(ParseError::MissingField("password")) .map(|password| { if password.is_empty() { None } else { Some(password.to_string().into()) } })?; Ok(Basic { user_id, password }) } } impl fmt::Debug for Basic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("Basic {}:******", self.user_id)) } } impl fmt::Display for Basic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("Basic {}:******", self.user_id)) } } impl TryIntoHeaderValue for Basic { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { let credential_length = self.user_id.len() + 1 + self.password.as_ref().map_or(0, |pwd| pwd.len()); // The length of BASE64 encoded bytes is `4 * credential_length.div_ceil(3)` // TODO: Use credential_length.div_ceil(3) when `int_roundings` becomes stable // https://github.com/rust-lang/rust/issues/88581 let mut value = String::with_capacity(6 + 4 * (credential_length + 2) / 3); let mut credentials = BytesMut::with_capacity(credential_length); credentials.extend_from_slice(self.user_id.as_bytes()); credentials.put_u8(b':'); if let Some(ref password) = self.password { credentials.extend_from_slice(password.as_bytes()); } value.push_str("Basic "); BASE64_STANDARD.encode_string(&credentials, &mut value); HeaderValue::from_maybe_shared(value) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_header() { let value = HeaderValue::from_static("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); let scheme = Basic::parse(&value); assert!(scheme.is_ok()); let scheme = scheme.unwrap(); assert_eq!(scheme.user_id, "Aladdin"); assert_eq!(scheme.password, Some("open sesame".into())); } #[test] fn test_empty_password() { let value = HeaderValue::from_static("Basic QWxhZGRpbjo="); let scheme = Basic::parse(&value); assert!(scheme.is_ok()); let scheme = scheme.unwrap(); assert_eq!(scheme.user_id, "Aladdin"); assert_eq!(scheme.password, None); } #[test] fn test_empty_header() { let value = HeaderValue::from_static(""); let scheme = Basic::parse(&value); assert!(scheme.is_err()); } #[test] fn test_wrong_scheme() { let value = HeaderValue::from_static("THOUSHALLNOTPASS please?"); let scheme = Basic::parse(&value); assert!(scheme.is_err()); } #[test] fn test_missing_credentials() { let value = HeaderValue::from_static("Basic "); let scheme = Basic::parse(&value); assert!(scheme.is_err()); } #[test] fn test_missing_credentials_colon() { let value = HeaderValue::from_static("Basic QWxsYWRpbg=="); let scheme = Basic::parse(&value); assert!(scheme.is_err()); } #[test] fn test_into_header_value() { let basic = Basic { user_id: "Aladdin".into(), password: Some("open sesame".into()), }; let result = basic.try_into_value(); assert!(result.is_ok()); assert_eq!( result.unwrap(), HeaderValue::from_static("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==") ); } } actix-web-httpauth-0.8.2/src/headers/authorization/scheme/bearer.rs000064400000000000000000000067371046102023000235460ustar 00000000000000use std::{borrow::Cow, fmt}; use actix_web::{ http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, web::{BufMut, BytesMut}, }; use crate::headers::authorization::{errors::ParseError, scheme::Scheme}; /// Credentials for `Bearer` authentication scheme, defined in [RFC 6750]. /// /// Should be used in combination with [`Authorization`] header. /// /// [RFC 6750]: https://tools.ietf.org/html/rfc6750 /// [`Authorization`]: crate::headers::authorization::Authorization #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] pub struct Bearer { token: Cow<'static, str>, } impl Bearer { /// Creates new `Bearer` credentials with the token provided. /// /// # Example /// ``` /// # use actix_web_httpauth::headers::authorization::Bearer; /// let credentials = Bearer::new("mF_9.B5f-4.1JqM"); /// ``` pub fn new(token: T) -> Bearer where T: Into>, { Bearer { token: token.into(), } } /// Gets reference to the credentials token. pub fn token(&self) -> &str { self.token.as_ref() } } impl Scheme for Bearer { fn parse(header: &HeaderValue) -> Result { // "Bearer *" length if header.len() < 8 { return Err(ParseError::Invalid); } let mut parts = header.to_str()?.splitn(2, ' '); match parts.next() { Some("Bearer") => {} _ => return Err(ParseError::MissingScheme), } let token = parts.next().ok_or(ParseError::Invalid)?; Ok(Bearer { token: token.to_string().into(), }) } } impl fmt::Debug for Bearer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("Bearer ******")) } } impl fmt::Display for Bearer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("Bearer {}", self.token)) } } impl TryIntoHeaderValue for Bearer { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { let mut buffer = BytesMut::with_capacity(7 + self.token.len()); buffer.put(&b"Bearer "[..]); buffer.extend_from_slice(self.token.as_bytes()); HeaderValue::from_maybe_shared(buffer.freeze()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_header() { let value = HeaderValue::from_static("Bearer mF_9.B5f-4.1JqM"); let scheme = Bearer::parse(&value); assert!(scheme.is_ok()); let scheme = scheme.unwrap(); assert_eq!(scheme.token, "mF_9.B5f-4.1JqM"); } #[test] fn test_empty_header() { let value = HeaderValue::from_static(""); let scheme = Bearer::parse(&value); assert!(scheme.is_err()); } #[test] fn test_wrong_scheme() { let value = HeaderValue::from_static("OAuthToken foo"); let scheme = Bearer::parse(&value); assert!(scheme.is_err()); } #[test] fn test_missing_token() { let value = HeaderValue::from_static("Bearer "); let scheme = Bearer::parse(&value); assert!(scheme.is_err()); } #[test] fn test_into_header_value() { let bearer = Bearer::new("mF_9.B5f-4.1JqM"); let result = bearer.try_into_value(); assert!(result.is_ok()); assert_eq!( result.unwrap(), HeaderValue::from_static("Bearer mF_9.B5f-4.1JqM") ); } } actix-web-httpauth-0.8.2/src/headers/authorization/scheme/mod.rs000064400000000000000000000007501046102023000230520ustar 00000000000000use std::fmt::{Debug, Display}; use actix_web::http::header::{HeaderValue, TryIntoHeaderValue}; pub mod basic; pub mod bearer; use crate::headers::authorization::errors::ParseError; /// Authentication scheme for [`Authorization`](super::Authorization) header. pub trait Scheme: TryIntoHeaderValue + Debug + Display + Clone + Send + Sync { /// Try to parse an authentication scheme from the `Authorization` header. fn parse(header: &HeaderValue) -> Result; } actix-web-httpauth-0.8.2/src/headers/mod.rs000064400000000000000000000001121046102023000166760ustar 00000000000000//! Typed HTTP headers. pub mod authorization; pub mod www_authenticate; actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/basic.rs000064400000000000000000000074621046102023000245230ustar 00000000000000//! Challenge for the "Basic" HTTP Authentication Scheme. use std::{borrow::Cow, fmt, str}; use actix_web::{ http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, web::{BufMut, Bytes, BytesMut}, }; use super::Challenge; use crate::utils; /// Challenge for [`WWW-Authenticate`] header with HTTP Basic auth scheme, /// described in [RFC 7617](https://tools.ietf.org/html/rfc7617) /// /// # Examples /// ``` /// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; /// use actix_web_httpauth::headers::www_authenticate::basic::Basic; /// use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; /// /// fn index(_req: HttpRequest) -> HttpResponse { /// let challenge = Basic::with_realm("Restricted area"); /// /// HttpResponse::Unauthorized() /// .insert_header(WwwAuthenticate(challenge)) /// .finish() /// } /// ``` /// /// [`WWW-Authenticate`]: ../struct.WwwAuthenticate.html #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Clone)] pub struct Basic { // "realm" parameter is optional now: https://tools.ietf.org/html/rfc7235#appendix-A pub(crate) realm: Option>, } impl Basic { /// Creates new `Basic` challenge with an empty `realm` field. /// /// # Examples /// ``` /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; /// let challenge = Basic::new(); /// ``` pub fn new() -> Basic { Default::default() } /// Creates new `Basic` challenge from the provided `realm` field value. /// /// # Examples /// /// ``` /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; /// let challenge = Basic::with_realm("Restricted area"); /// ``` /// /// ``` /// # use actix_web_httpauth::headers::www_authenticate::basic::Basic; /// let my_realm = "Earth realm".to_string(); /// let challenge = Basic::with_realm(my_realm); /// ``` pub fn with_realm(value: T) -> Basic where T: Into>, { Basic { realm: Some(value.into()), } } } #[doc(hidden)] impl Challenge for Basic { fn to_bytes(&self) -> Bytes { // 5 is for `"Basic"`, 9 is for `"realm=\"\""` let length = 5 + self.realm.as_ref().map_or(0, |realm| realm.len() + 9); let mut buffer = BytesMut::with_capacity(length); buffer.put(&b"Basic"[..]); if let Some(ref realm) = self.realm { buffer.put(&b" realm=\""[..]); utils::put_quoted(&mut buffer, realm); buffer.put_u8(b'"'); } buffer.freeze() } } impl fmt::Display for Basic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let bytes = self.to_bytes(); let repr = str::from_utf8(&bytes) // Should not happen since challenges are crafted manually // from a `&'static str` or `String` .map_err(|_| fmt::Error)?; f.write_str(repr) } } impl TryIntoHeaderValue for Basic { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { HeaderValue::from_maybe_shared(self.to_bytes()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_plain_into_header_value() { let challenge = Basic { realm: None }; let value = challenge.try_into_value(); assert!(value.is_ok()); let value = value.unwrap(); assert_eq!(value, "Basic"); } #[test] fn test_with_realm_into_header_value() { let challenge = Basic { realm: Some("Restricted area".into()), }; let value = challenge.try_into_value(); assert!(value.is_ok()); let value = value.unwrap(); assert_eq!(value, "Basic realm=\"Restricted area\""); } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/bearer/builder.rs000064400000000000000000000040201046102023000263130ustar 00000000000000use std::borrow::Cow; use super::{Bearer, Error}; /// Builder for the [`Bearer`] challenge. /// /// It is up to implementor to fill all required fields, neither this `Builder` nor [`Bearer`] /// provide any validation. #[derive(Debug, Default)] pub struct BearerBuilder(Bearer); impl BearerBuilder { /// Provides the `scope` attribute, as defined in [RFC 6749 §3.3]. /// /// [RFC 6749 §3.3]: https://tools.ietf.org/html/rfc6749#section-3.3 pub fn scope(mut self, value: T) -> Self where T: Into>, { self.0.scope = Some(value.into()); self } /// Provides the `realm` attribute, as defined in [RFC 2617]. /// /// [RFC 2617]: https://tools.ietf.org/html/rfc2617 pub fn realm(mut self, value: T) -> Self where T: Into>, { self.0.realm = Some(value.into()); self } /// Provides the `error` attribute, as defined in [RFC 6750, Section 3.1]. /// /// [RFC 6750 §3.1]: https://tools.ietf.org/html/rfc6750#section-3.1 pub fn error(mut self, value: Error) -> Self { self.0.error = Some(value); self } /// Provides the `error_description` attribute, as defined in [RFC 6750, Section 3]. /// /// [RFC 6750 §3]: https://tools.ietf.org/html/rfc6750#section-3 pub fn error_description(mut self, value: T) -> Self where T: Into>, { self.0.error_description = Some(value.into()); self } /// Provides the `error_uri` attribute, as defined in [RFC 6750 §3]. /// /// It is up to implementor to provide properly-formed absolute URI. /// /// [RFC 6750 §3](https://tools.ietf.org/html/rfc6750#section-3) pub fn error_uri(mut self, value: T) -> Self where T: Into>, { self.0.error_uri = Some(value.into()); self } /// Consumes the builder and returns built `Bearer` instance. pub fn finish(self) -> Bearer { self.0 } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/bearer/challenge.rs000064400000000000000000000105431046102023000266160ustar 00000000000000use std::{borrow::Cow, fmt, str}; use actix_web::{ http::header::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, web::{BufMut, Bytes, BytesMut}, }; use super::{super::Challenge, BearerBuilder, Error}; use crate::utils; /// Challenge for [`WWW-Authenticate`] header with HTTP Bearer auth scheme, described in [RFC 6750]. /// /// # Examples /// ``` /// # use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; /// use actix_web_httpauth::headers::www_authenticate::bearer::{ /// Bearer, Error, /// }; /// use actix_web_httpauth::headers::www_authenticate::WwwAuthenticate; /// /// fn index(_req: HttpRequest) -> HttpResponse { /// let challenge = Bearer::build() /// .realm("example") /// .scope("openid profile email") /// .error(Error::InvalidToken) /// .error_description("The access token expired") /// .error_uri("http://example.org") /// .finish(); /// /// HttpResponse::Unauthorized() /// .insert_header(WwwAuthenticate(challenge)) /// .finish() /// } /// ``` /// /// [`WWW-Authenticate`]: crate::headers::www_authenticate::WwwAuthenticate /// [RFC 6750]: https://tools.ietf.org/html/rfc6750#section-3 #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Bearer { pub(crate) scope: Option>, pub(crate) realm: Option>, pub(crate) error: Option, pub(crate) error_description: Option>, pub(crate) error_uri: Option>, } impl Bearer { /// Creates the builder for `Bearer` challenge. /// /// # Examples /// ``` /// # use actix_web_httpauth::headers::www_authenticate::bearer::{Bearer}; /// let challenge = Bearer::build() /// .realm("Restricted area") /// .scope("openid profile email") /// .finish(); /// ``` pub fn build() -> BearerBuilder { BearerBuilder::default() } } #[doc(hidden)] impl Challenge for Bearer { fn to_bytes(&self) -> Bytes { let desc_uri_required = self .error_description .as_ref() .map_or(0, |desc| desc.len() + 20) + self.error_uri.as_ref().map_or(0, |url| url.len() + 12); let capacity = 6 + self.realm.as_ref().map_or(0, |realm| realm.len() + 9) + self.scope.as_ref().map_or(0, |scope| scope.len() + 9) + desc_uri_required; let mut buffer = BytesMut::with_capacity(capacity); buffer.put(&b"Bearer"[..]); if let Some(ref realm) = self.realm { buffer.put(&b" realm=\""[..]); utils::put_quoted(&mut buffer, realm); buffer.put_u8(b'"'); } if let Some(ref scope) = self.scope { buffer.put(&b" scope=\""[..]); utils::put_quoted(&mut buffer, scope); buffer.put_u8(b'"'); } if let Some(ref error) = self.error { let error_repr = error.as_str(); let remaining = buffer.remaining_mut(); let required = desc_uri_required + error_repr.len() + 9; // 9 is for `" error=\"\""` if remaining < required { buffer.reserve(required); } buffer.put(&b" error=\""[..]); utils::put_quoted(&mut buffer, error_repr); buffer.put_u8(b'"') } if let Some(ref error_description) = self.error_description { buffer.put(&b" error_description=\""[..]); utils::put_quoted(&mut buffer, error_description); buffer.put_u8(b'"'); } if let Some(ref error_uri) = self.error_uri { buffer.put(&b" error_uri=\""[..]); utils::put_quoted(&mut buffer, error_uri); buffer.put_u8(b'"'); } buffer.freeze() } } impl fmt::Display for Bearer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let bytes = self.to_bytes(); let repr = str::from_utf8(&bytes) // Should not happen since challenges are crafted manually // from `&'static str`'s and Strings .map_err(|_| fmt::Error)?; f.write_str(repr) } } impl TryIntoHeaderValue for Bearer { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { HeaderValue::from_maybe_shared(self.to_bytes()) } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/bearer/errors.rs000064400000000000000000000031731046102023000262110ustar 00000000000000use std::fmt; use actix_web::http::StatusCode; /// Bearer authorization error types, described in [RFC 6750]. /// /// [RFC 6750]: https://tools.ietf.org/html/rfc6750#section-3.1 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Error { /// The request is missing a required parameter, includes an unsupported parameter or parameter /// value, repeats the same parameter, uses more than one method for including an access token, /// or is otherwise malformed. InvalidRequest, /// The access token provided is expired, revoked, malformed, or invalid for other reasons. InvalidToken, /// The request requires higher privileges than provided by the access token. InsufficientScope, } impl Error { /// Returns [HTTP status code] suitable for current error type. /// /// [HTTP status code]: `actix_web::http::StatusCode` #[allow(clippy::trivially_copy_pass_by_ref)] pub fn status_code(&self) -> StatusCode { match self { Error::InvalidRequest => StatusCode::BAD_REQUEST, Error::InvalidToken => StatusCode::UNAUTHORIZED, Error::InsufficientScope => StatusCode::FORBIDDEN, } } #[doc(hidden)] #[allow(clippy::trivially_copy_pass_by_ref)] pub fn as_str(&self) -> &str { match self { Error::InvalidRequest => "invalid_request", Error::InvalidToken => "invalid_token", Error::InsufficientScope => "insufficient_scope", } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/bearer/mod.rs000064400000000000000000000011601046102023000254460ustar 00000000000000//! Challenge for the "Bearer" HTTP Authentication Scheme. mod builder; mod challenge; mod errors; pub use self::{builder::BearerBuilder, challenge::Bearer, errors::Error}; #[cfg(test)] mod tests { use super::*; #[test] fn to_bytes() { let bearer = Bearer::build() .error(Error::InvalidToken) .error_description("Subject 8740827c-2e0a-447b-9716-d73042e4039d not found") .finish(); assert_eq!( "Bearer error=\"invalid_token\" error_description=\"Subject 8740827c-2e0a-447b-9716-d73042e4039d not found\"", format!("{bearer}") ); } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/challenge/mod.rs000064400000000000000000000005761046102023000242200ustar 00000000000000use std::fmt::{Debug, Display}; use actix_web::{http::header::TryIntoHeaderValue, web::Bytes}; pub mod basic; pub mod bearer; /// Authentication challenge for `WWW-Authenticate` header. pub trait Challenge: TryIntoHeaderValue + Debug + Display + Clone + Send + Sync { /// Converts the challenge into a bytes suitable for HTTP transmission. fn to_bytes(&self) -> Bytes; } actix-web-httpauth-0.8.2/src/headers/www_authenticate/header.rs000064400000000000000000000020111046102023000227310ustar 00000000000000use actix_web::{ error::ParseError, http::header::{Header, HeaderName, HeaderValue, TryIntoHeaderValue, WWW_AUTHENTICATE}, HttpMessage, }; use super::Challenge; /// `WWW-Authenticate` header, described in [RFC 7235]. /// /// This header is generic over the [`Challenge`] trait, see [`Basic`](super::basic::Basic) and /// [`Bearer`](super::bearer::Bearer) challenges for details. /// /// [RFC 7235]: https://tools.ietf.org/html/rfc7235#section-4.1 #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WwwAuthenticate(pub C); impl Header for WwwAuthenticate { #[inline] fn name() -> HeaderName { WWW_AUTHENTICATE } fn parse(_msg: &T) -> Result { unimplemented!() } } impl TryIntoHeaderValue for WwwAuthenticate { type Error = ::Error; fn try_into_value(self) -> Result { self.0.try_into_value() } } actix-web-httpauth-0.8.2/src/headers/www_authenticate/mod.rs000064400000000000000000000002631046102023000222670ustar 00000000000000//! `WWW-Authenticate` header and various auth challenges. mod challenge; mod header; pub use self::{ challenge::{basic, bearer, Challenge}, header::WwwAuthenticate, }; actix-web-httpauth-0.8.2/src/lib.rs000064400000000000000000000017561046102023000152710ustar 00000000000000//! HTTP authentication schemes for [Actix Web](https://actix.rs). //! //! Provides: //! - Typed [Authorization] and [WWW-Authenticate] headers //! - [Extractors] for an [Authorization] header //! - [Middleware] for easier authorization checking //! //! ## Supported schemes //! - `Bearer` as defined in [RFC 6750](https://tools.ietf.org/html/rfc6750). //! - `Basic` as defined in [RFC 7617](https://tools.ietf.org/html/rfc7617). //! //! [Authorization]: `self::headers::authorization::Authorization` //! [WWW-Authenticate]: `self::headers::www_authenticate::WwwAuthenticate` //! [Extractors]: https://actix.rs/docs/extractors //! [Middleware]: self::middleware #![forbid(unsafe_code)] #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible, missing_docs)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod extractors; pub mod headers; pub mod middleware; mod utils; actix-web-httpauth-0.8.2/src/middleware.rs000064400000000000000000000360171046102023000166360ustar 00000000000000//! HTTP Authentication middleware. use std::{ future::Future, marker::PhantomData, pin::Pin, rc::Rc, sync::Arc, task::{Context, Poll}, }; use actix_web::{ body::{EitherBody, MessageBody}, dev::{Service, ServiceRequest, ServiceResponse, Transform}, Error, FromRequest, }; use futures_core::ready; use futures_util::future::{self, LocalBoxFuture, TryFutureExt as _}; use crate::extractors::{basic, bearer}; /// Middleware for checking HTTP authentication. /// /// If there is no `Authorization` header in the request, this middleware returns an error /// immediately, without calling the `F` callback. /// /// Otherwise, it will pass both the request and the parsed credentials into it. In case of /// successful validation `F` callback is required to return the `ServiceRequest` back. #[derive(Debug, Clone)] pub struct HttpAuthentication where T: FromRequest, { process_fn: Arc, _extractor: PhantomData, } impl HttpAuthentication where T: FromRequest, F: Fn(ServiceRequest, T) -> O, O: Future>, { /// Construct `HttpAuthentication` middleware with the provided auth extractor `T` and /// validation callback `F`. /// /// This function can be used to implement optional authentication and/or custom responses to /// missing authentication. /// /// # Examples /// /// ## Required Basic Auth /// /// ```no_run /// # use actix_web_httpauth::extractors::basic::BasicAuth; /// # use actix_web::dev::ServiceRequest; /// async fn validator( /// req: ServiceRequest, /// credentials: BasicAuth, /// ) -> Result { /// eprintln!("{credentials:?}"); /// /// if credentials.user_id().contains('x') { /// return Err((actix_web::error::ErrorBadRequest("user ID contains x"), req)); /// } /// /// Ok(req) /// } /// # actix_web_httpauth::middleware::HttpAuthentication::with_fn(validator); /// ``` /// /// ## Optional Bearer Auth /// /// ```no_run /// # use actix_web_httpauth::extractors::bearer::BearerAuth; /// # use actix_web::dev::ServiceRequest; /// async fn validator( /// req: ServiceRequest, /// credentials: Option, /// ) -> Result { /// let Some(credentials) = credentials else { /// return Err((actix_web::error::ErrorBadRequest("no bearer header"), req)); /// }; /// /// eprintln!("{credentials:?}"); /// /// if credentials.token().contains('x') { /// return Err((actix_web::error::ErrorBadRequest("token contains x"), req)); /// } /// /// Ok(req) /// } /// # actix_web_httpauth::middleware::HttpAuthentication::with_fn(validator); /// ``` pub fn with_fn(process_fn: F) -> HttpAuthentication { HttpAuthentication { process_fn: Arc::new(process_fn), _extractor: PhantomData, } } } impl HttpAuthentication where F: Fn(ServiceRequest, basic::BasicAuth) -> O, O: Future>, { /// Construct `HttpAuthentication` middleware for the HTTP "Basic" authentication scheme. /// /// # Examples /// ``` /// # use actix_web::{Error, dev::ServiceRequest}; /// # use actix_web_httpauth::{extractors::basic::BasicAuth, middleware::HttpAuthentication}; /// // In this example validator returns immediately, but since it is required to return /// // anything that implements `IntoFuture` trait, it can be extended to query database or to /// // do something else in a async manner. /// async fn validator( /// req: ServiceRequest, /// credentials: BasicAuth, /// ) -> Result { /// // All users are great and more than welcome! /// Ok(req) /// } /// /// let middleware = HttpAuthentication::basic(validator); /// ``` pub fn basic(process_fn: F) -> Self { Self::with_fn(process_fn) } } impl HttpAuthentication where F: Fn(ServiceRequest, bearer::BearerAuth) -> O, O: Future>, { /// Construct `HttpAuthentication` middleware for the HTTP "Bearer" authentication scheme. /// /// # Examples /// ``` /// # use actix_web::{Error, dev::ServiceRequest}; /// # use actix_web_httpauth::{ /// # extractors::{AuthenticationError, AuthExtractorConfig, bearer::{self, BearerAuth}}, /// # middleware::HttpAuthentication, /// # }; /// async fn validator( /// req: ServiceRequest, /// credentials: BearerAuth /// ) -> Result { /// if credentials.token() == "mF_9.B5f-4.1JqM" { /// Ok(req) /// } else { /// let config = req.app_data::() /// .cloned() /// .unwrap_or_default() /// .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13"); /// /// Err((AuthenticationError::from(config).into(), req)) /// } /// } /// /// let middleware = HttpAuthentication::bearer(validator); /// ``` pub fn bearer(process_fn: F) -> Self { Self::with_fn(process_fn) } } impl Transform for HttpAuthentication where S: Service, Error = Error> + 'static, S::Future: 'static, F: Fn(ServiceRequest, T) -> O + 'static, O: Future> + 'static, T: FromRequest + 'static, B: MessageBody + 'static, { type Response = ServiceResponse>; type Error = Error; type Transform = AuthenticationMiddleware; type InitError = (); type Future = future::Ready>; fn new_transform(&self, service: S) -> Self::Future { future::ok(AuthenticationMiddleware { service: Rc::new(service), process_fn: self.process_fn.clone(), _extractor: PhantomData, }) } } #[doc(hidden)] pub struct AuthenticationMiddleware where T: FromRequest, { service: Rc, process_fn: Arc, _extractor: PhantomData, } impl Service for AuthenticationMiddleware where S: Service, Error = Error> + 'static, S::Future: 'static, F: Fn(ServiceRequest, T) -> O + 'static, O: Future> + 'static, T: FromRequest + 'static, B: MessageBody + 'static, { type Response = ServiceResponse>; type Error = S::Error; type Future = LocalBoxFuture<'static, Result>; actix_web::dev::forward_ready!(service); fn call(&self, req: ServiceRequest) -> Self::Future { let process_fn = Arc::clone(&self.process_fn); let service = Rc::clone(&self.service); Box::pin(async move { let (req, credentials) = match Extract::::new(req).await { Ok(req) => req, Err((err, req)) => { return Ok(req.error_response(err).map_into_right_body()); } }; let req = match process_fn(req, credentials).await { Ok(req) => req, Err((err, req)) => { return Ok(req.error_response(err).map_into_right_body()); } }; service.call(req).await.map(|res| res.map_into_left_body()) }) } } struct Extract { req: Option, fut: Option>>, _extractor: PhantomData T>, } impl Extract { pub fn new(req: ServiceRequest) -> Self { Extract { req: Some(req), fut: None, _extractor: PhantomData, } } } impl Future for Extract where T: FromRequest, T::Future: 'static, T::Error: 'static, { type Output = Result<(ServiceRequest, T), (Error, ServiceRequest)>; fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { if self.fut.is_none() { let req = self.req.as_mut().expect("Extract future was polled twice!"); let fut = req.extract::().map_err(Into::into); self.fut = Some(Box::pin(fut)); } let fut = self .fut .as_mut() .expect("Extraction future should be initialized at this point"); let credentials = ready!(fut.as_mut().poll(ctx)).map_err(|err| { ( err, // returning request allows a proper error response to be created self.req.take().expect("Extract future was polled twice!"), ) })?; let req = self.req.take().expect("Extract future was polled twice!"); Poll::Ready(Ok((req, credentials))) } } #[cfg(test)] mod tests { use actix_service::into_service; use actix_web::{ error::{self, ErrorForbidden}, http::StatusCode, test::TestRequest, web, App, HttpResponse, }; use super::*; use crate::extractors::{basic::BasicAuth, bearer::BearerAuth}; /// This is a test for https://github.com/actix/actix-extras/issues/10 #[actix_web::test] async fn test_middleware_panic() { let middleware = AuthenticationMiddleware { service: Rc::new(into_service(|_: ServiceRequest| async move { actix_web::rt::time::sleep(std::time::Duration::from_secs(1)).await; Err::(error::ErrorBadRequest("error")) })), process_fn: Arc::new(|req, _: BearerAuth| async { Ok(req) }), _extractor: PhantomData, }; let req = TestRequest::get() .append_header(("Authorization", "Bearer 1")) .to_srv_request(); let f = middleware.call(req).await; let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; assert!(f.is_err()); } /// This is a test for https://github.com/actix/actix-extras/issues/10 #[actix_web::test] async fn test_middleware_panic_several_orders() { let middleware = AuthenticationMiddleware { service: Rc::new(into_service(|_: ServiceRequest| async move { actix_web::rt::time::sleep(std::time::Duration::from_secs(1)).await; Err::(error::ErrorBadRequest("error")) })), process_fn: Arc::new(|req, _: BearerAuth| async { Ok(req) }), _extractor: PhantomData, }; let req = TestRequest::get() .append_header(("Authorization", "Bearer 1")) .to_srv_request(); let f1 = middleware.call(req).await; let req = TestRequest::get() .append_header(("Authorization", "Bearer 1")) .to_srv_request(); let f2 = middleware.call(req).await; let req = TestRequest::get() .append_header(("Authorization", "Bearer 1")) .to_srv_request(); let f3 = middleware.call(req).await; let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; assert!(f1.is_err()); assert!(f2.is_err()); assert!(f3.is_err()); } #[actix_web::test] async fn test_middleware_opt_extractor() { let middleware = AuthenticationMiddleware { service: Rc::new(into_service(|req: ServiceRequest| async move { Ok::(req.into_response(HttpResponse::Ok().finish())) })), process_fn: Arc::new(|req, auth: Option| { assert!(auth.is_none()); async { Ok(req) } }), _extractor: PhantomData, }; let req = TestRequest::get() .append_header(("Authorization996", "Bearer 1")) .to_srv_request(); let f = middleware.call(req).await; let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; assert!(f.is_ok()); } #[actix_web::test] async fn test_middleware_res_extractor() { let middleware = AuthenticationMiddleware { service: Rc::new(into_service(|req: ServiceRequest| async move { Ok::(req.into_response(HttpResponse::Ok().finish())) })), process_fn: Arc::new( |req, auth: Result::Error>| { assert!(auth.is_err()); async { Ok(req) } }, ), _extractor: PhantomData, }; let req = TestRequest::get() .append_header(("Authorization", "BearerLOL")) .to_srv_request(); let f = middleware.call(req).await; let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await; assert!(f.is_ok()); } #[actix_web::test] async fn test_middleware_works_with_app() { async fn validator( req: ServiceRequest, _credentials: BasicAuth, ) -> Result { Err((ErrorForbidden("You are not welcome!"), req)) } let middleware = HttpAuthentication::basic(validator); let srv = actix_web::test::init_service( App::new() .wrap(middleware) .route("/", web::get().to(HttpResponse::Ok)), ) .await; let req = actix_web::test::TestRequest::with_uri("/") .append_header(("Authorization", "Basic DontCare")) .to_request(); let resp = srv.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } #[actix_web::test] async fn test_middleware_works_with_scope() { async fn validator( req: ServiceRequest, _credentials: BasicAuth, ) -> Result { Err((ErrorForbidden("You are not welcome!"), req)) } let middleware = actix_web::middleware::Compat::new(HttpAuthentication::basic(validator)); let srv = actix_web::test::init_service( App::new().service( web::scope("/") .wrap(middleware) .route("/", web::get().to(HttpResponse::Ok)), ), ) .await; let req = actix_web::test::TestRequest::with_uri("/") .append_header(("Authorization", "Basic DontCare")) .to_request(); let resp = srv.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } } actix-web-httpauth-0.8.2/src/utils.rs000064400000000000000000000052011046102023000156500ustar 00000000000000use std::str; use actix_web::web::BytesMut; enum State { YieldStr, YieldQuote, } struct Quoted<'a> { inner: ::std::iter::Peekable>, state: State, } impl<'a> Quoted<'a> { pub fn new(s: &'a str) -> Quoted<'_> { Quoted { inner: s.split('"').peekable(), state: State::YieldStr, } } } impl<'a> Iterator for Quoted<'a> { type Item = &'a str; fn next(&mut self) -> Option { match self.state { State::YieldStr => match self.inner.next() { Some(val) => { self.state = State::YieldQuote; Some(val) } None => None, }, State::YieldQuote => match self.inner.peek() { Some(_) => { self.state = State::YieldStr; Some("\\\"") } None => None, }, } } } /// Escapes the quotes in `val`. pub fn put_quoted(buf: &mut BytesMut, val: &str) { for part in Quoted::new(val) { buf.extend_from_slice(part.as_bytes()); } } #[cfg(test)] mod tests { use std::str; use actix_web::web::BytesMut; use super::put_quoted; #[test] fn test_quote_str() { let input = "a \"quoted\" string"; let mut output = BytesMut::new(); put_quoted(&mut output, input); let result = str::from_utf8(&output).unwrap(); assert_eq!(result, "a \\\"quoted\\\" string"); } #[test] fn test_without_quotes() { let input = "non-quoted string"; let mut output = BytesMut::new(); put_quoted(&mut output, input); let result = str::from_utf8(&output).unwrap(); assert_eq!(result, "non-quoted string"); } #[test] fn test_starts_with_quote() { let input = "\"first-quoted string"; let mut output = BytesMut::new(); put_quoted(&mut output, input); let result = str::from_utf8(&output).unwrap(); assert_eq!(result, "\\\"first-quoted string"); } #[test] fn test_ends_with_quote() { let input = "last-quoted string\""; let mut output = BytesMut::new(); put_quoted(&mut output, input); let result = str::from_utf8(&output).unwrap(); assert_eq!(result, "last-quoted string\\\""); } #[test] fn test_double_quote() { let input = "quote\"\"string"; let mut output = BytesMut::new(); put_quoted(&mut output, input); let result = str::from_utf8(&output).unwrap(); assert_eq!(result, "quote\\\"\\\"string"); } }