commit b4d0d95f8e5162da87c6bcffa6a7c0541b60b1b0
parent 4bcc69534030ceb2b7fefee87c2307dabf453813
Author: minerva-jupiter <ryouturn@gmail.com>
Date: Sat, 25 Jul 2026 17:58:54 +0900
feat(nfc): implement nfc sender client and member directory lookup
Implement the NFC sender application logic to poll for NFC cards, map card serial numbers to user IDs via a directory, and toggle user presence statuses through the lab core API. Also update shared data structures and add comprehensive integration tests.
Diffstat:
8 files changed, 1010 insertions(+), 181 deletions(-)
diff --git a/.env b/.env
@@ -1,2 +1,6 @@
+CORE_URL="https://localhost:8443"
SALT="your_secure_salt_here"
ALLOWED_DEVICES='{"nfc": "nfc", "remote": "remote"}'
+MEMBER_DIRECTORY='{
+ [{"id":"s241033","card_serial_number":"123456789"}]
+}'
diff --git a/Cargo.lock b/Cargo.lock
@@ -8,7 +8,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"bytes",
"futures-core",
"futures-sink",
@@ -30,8 +30,8 @@ dependencies = [
"actix-service",
"actix-tls",
"actix-utils",
- "base64",
- "bitflags",
+ "base64 0.22.1",
+ "bitflags 2.13.1",
"brotli",
"bytes",
"bytestring",
@@ -40,8 +40,8 @@ dependencies = [
"flate2",
"foldhash",
"futures-core",
- "h2",
- "http",
+ "h2 0.3.27",
+ "http 0.2.12",
"httparse",
"httpdate",
"itoa",
@@ -66,7 +66,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -77,7 +77,7 @@ checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
dependencies = [
"bytestring",
"cfg-if",
- "http",
+ "http 0.2.12",
"regex",
"regex-lite",
"serde",
@@ -169,13 +169,13 @@ dependencies = [
"bytes",
"bytestring",
"cfg-if",
- "cookie",
+ "cookie 0.16.2",
"derive_more",
"encoding_rs",
"foldhash",
"futures-core",
"futures-util",
- "impl-more 0.3.1",
+ "impl-more 0.3.5",
"itoa",
"language-tags",
"log",
@@ -188,7 +188,7 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"smallvec",
- "socket2 0.6.4",
+ "socket2 0.6.5",
"time",
"tracing",
"url",
@@ -203,7 +203,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -237,6 +237,56 @@ dependencies = [
]
[[package]]
+name = "anstream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "asn1-rs"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -260,7 +310,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
"synstructure",
]
@@ -272,10 +322,26 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "assert-json-diff"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
+dependencies = [
+ "serde",
+ "serde_json",
]
[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -283,9 +349,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
-version = "1.17.1"
+version = "1.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad"
+checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -293,9 +359,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.42.0"
+version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444"
+checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
dependencies = [
"cc",
"cmake",
@@ -311,6 +377,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
+name = "base64"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9"
+
+[[package]]
name = "bit-vec"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,9 +393,15 @@ dependencies = [
[[package]]
name = "bitflags"
-version = "2.13.0"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "block-buffer"
@@ -357,9 +435,9 @@ dependencies = [
[[package]]
name = "bytes"
-version = "1.12.0"
+version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "bytestring"
@@ -372,9 +450,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.65"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
+checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -409,6 +487,12 @@ dependencies = [
]
[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
+[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -435,6 +519,35 @@ dependencies = [
]
[[package]]
+name = "cookie"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie_store"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
+dependencies = [
+ "cookie 0.18.1",
+ "document-features",
+ "idna",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
+[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -468,6 +581,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
+name = "deadpool"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b"
+dependencies = [
+ "deadpool-runtime",
+ "lazy_static",
+ "num_cpus",
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-runtime"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
+
+[[package]]
+name = "defmt"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
+dependencies = [
+ "bitflags 1.3.2",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
+dependencies = [
+ "defmt-parser",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
name = "der-parser"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -506,7 +668,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
- "syn",
+ "syn 2.0.119",
"unicode-xid",
]
@@ -529,7 +691,16 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
]
[[package]]
@@ -539,6 +710,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
+name = "dotenvy"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+
+[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -554,16 +731,26 @@ dependencies = [
]
[[package]]
-name = "env_logger"
-version = "0.10.2"
+name = "env_filter"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217"
dependencies = [
- "humantime",
- "is-terminal",
"log",
"regex",
- "termcolor",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "jiff",
+ "log",
]
[[package]]
@@ -626,31 +813,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
+name = "futures"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
name = "futures-core"
-version = "0.3.32"
+version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
[[package]]
name = "futures-sink"
-version = "0.3.32"
+version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
[[package]]
name = "futures-task"
-version = "0.3.32"
+version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
-version = "0.3.32"
+version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
+ "futures-channel",
"futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"slab",
]
@@ -668,25 +913,13 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi 5.3.0",
- "wasip2",
-]
-
-[[package]]
-name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
- "r-efi 6.0.0",
+ "r-efi",
"rand_core",
]
@@ -701,7 +934,26 @@ dependencies = [
"futures-core",
"futures-sink",
"futures-util",
- "http",
+ "http 0.2.12",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.4.2",
"indexmap",
"slab",
"tokio",
@@ -733,6 +985,39 @@ dependencies = [
]
[[package]]
+name = "http"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
+dependencies = [
+ "bytes",
+ "http 1.4.2",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http 1.4.2",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -745,12 +1030,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
-name = "humantime"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
-
-[[package]]
name = "hybrid-array"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -760,6 +1039,42 @@ dependencies = [
]
[[package]]
+name = "hyper"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2 0.4.15",
+ "http 1.4.2",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "bytes",
+ "http 1.4.2",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -870,9 +1185,9 @@ checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
[[package]]
name = "impl-more"
-version = "0.3.1"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870"
+checksum = "277ff51754a3f68f12f58446c5d006aa8baa4914ea273cce24a599cfaff33d4f"
[[package]]
name = "indexmap"
@@ -885,15 +1200,10 @@ dependencies = [
]
[[package]]
-name = "is-terminal"
-version = "0.4.17"
+name = "is_terminal_polyfill"
+version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys 0.61.2",
-]
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
@@ -902,12 +1212,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
+name = "jiff"
+version = "0.2.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e184d09547b80eb7e20d141ba2fb1fbac843ca53f4cf1b31210adc4c1adc6e16"
+dependencies = [
+ "defmt",
+ "jiff-core",
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde_core",
+]
+
+[[package]]
+name = "jiff-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
+dependencies = [
+ "defmt",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "323da076b7a6faf914dc677cb05a4b907742ff7375c8322c9e7f5061e5e0e9de"
+dependencies = [
+ "jiff-core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
name = "jobserver"
-version = "0.1.34"
+version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
- "getrandom 0.3.4",
+ "getrandom 0.4.3",
"libc",
]
@@ -916,7 +1262,7 @@ name = "lab_core"
version = "0.1.0"
dependencies = [
"actix-web",
- "base64",
+ "base64 0.23.0",
"dotenv",
"env_logger",
"log",
@@ -943,9 +1289,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.186"
+version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "litemap"
@@ -954,6 +1300,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
+[[package]]
name = "local-channel"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -987,9 +1339,9 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "memchr"
-version = "2.8.2"
+version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "mime"
@@ -1015,9 +1367,9 @@ dependencies = [
[[package]]
name = "mio"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
+checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
dependencies = [
"libc",
"log",
@@ -1028,6 +1380,15 @@ dependencies = [
[[package]]
name = "nfc_sender"
version = "0.1.0"
+dependencies = [
+ "dotenvy",
+ "pcsc",
+ "serde_json",
+ "shared",
+ "tokio",
+ "ureq",
+ "wiremock",
+]
[[package]]
name = "nom"
@@ -1074,6 +1435,16 @@ dependencies = [
]
[[package]]
+name = "num_cpus"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
name = "oid-registry"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1089,6 +1460,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1112,12 +1489,31 @@ dependencies = [
]
[[package]]
+name = "pcsc"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da"
+dependencies = [
+ "bitflags 2.13.1",
+ "pcsc-sys",
+]
+
+[[package]]
+name = "pcsc-sys"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ef017e15d2e5592a9e39a346c1dbaea5120bab7ed7106b210ef58ebd97003"
+dependencies = [
+ "pkg-config",
+]
+
+[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
- "base64",
+ "base64 0.22.1",
"serde_core",
]
@@ -1140,6 +1536,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
+name = "portable-atomic"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1156,30 +1567,24 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
-version = "1.0.106"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.46"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
-[[package]]
-name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
@@ -1221,14 +1626,14 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
]
[[package]]
name = "regex"
-version = "1.12.4"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
+checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
@@ -1238,9 +1643,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.14"
+version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
dependencies = [
"aho-corasick",
"memchr",
@@ -1293,13 +1698,14 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.41"
+version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
+checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"aws-lc-rs",
"log",
"once_cell",
+ "ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -1317,9 +1723,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.15.0"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046"
+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [
"zeroize",
]
@@ -1356,9 +1762,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
-version = "1.0.228"
+version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
@@ -1366,29 +1772,29 @@ dependencies = [
[[package]]
name = "serde_core"
-version = "1.0.228"
+version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.228"
+version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 3.0.3",
]
[[package]]
name = "serde_json"
-version = "1.0.150"
+version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
@@ -1459,9 +1865,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
-version = "0.3.9"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "slab"
@@ -1487,9 +1893,9 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.6.4"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
"windows-sys 0.61.2",
@@ -1509,9 +1915,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "2.0.118"
+version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
@@ -1519,50 +1925,52 @@ dependencies = [
]
[[package]]
-name = "synstructure"
-version = "0.13.2"
+name = "syn"
+version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "unicode-ident",
]
[[package]]
-name = "termcolor"
-version = "1.4.1"
+name = "synstructure"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
- "winapi-util",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
]
[[package]]
name = "thiserror"
-version = "2.0.18"
+version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.18"
+version = "2.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 3.0.3",
]
[[package]]
name = "time"
-version = "0.3.53"
+version = "0.3.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
+checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
dependencies = [
"deranged",
"num-conv",
@@ -1580,9 +1988,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
-version = "0.2.31"
+version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f"
+checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
dependencies = [
"num-conv",
"time-core",
@@ -1600,9 +2008,9 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.52.3"
+version = "1.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
dependencies = [
"bytes",
"libc",
@@ -1610,11 +2018,23 @@ dependencies = [
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "socket2 0.6.4",
+ "socket2 0.6.5",
+ "tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
+name = "tokio-macros"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1626,13 +2046,14 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.18"
+version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
+checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
+ "libc",
"pin-project-lite",
"tokio",
]
@@ -1657,7 +2078,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
@@ -1670,6 +2091,12 @@ dependencies = [
]
[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1700,6 +2127,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
+name = "ureq"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
+dependencies = [
+ "base64 0.22.1",
+ "cookie_store",
+ "flate2",
+ "log",
+ "percent-encoding",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "ureq-proto",
+ "utf8-zero",
+ "webpki-roots",
+]
+
+[[package]]
+name = "ureq-proto"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
+dependencies = [
+ "base64 0.22.1",
+ "http 1.4.2",
+ "httparse",
+ "log",
+]
+
+[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1712,39 +2171,51 @@ dependencies = [
]
[[package]]
+name = "utf8-zero"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
+
+[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
+name = "want"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
[[package]]
-name = "wasip2"
-version = "1.0.4+wasi-0.2.12"
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
-dependencies = [
- "wit-bindgen",
-]
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
-name = "winapi-util"
-version = "0.1.11"
+name = "webpki-roots"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
- "windows-sys 0.61.2",
+ "rustls-pki-types",
]
[[package]]
@@ -1836,10 +2307,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
-name = "wit-bindgen"
-version = "0.57.1"
+name = "wiremock"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031"
+dependencies = [
+ "assert-json-diff",
+ "base64 0.22.1",
+ "deadpool",
+ "futures",
+ "http 1.4.2",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "log",
+ "once_cell",
+ "regex",
+ "serde",
+ "serde_json",
+ "tokio",
+ "url",
+]
[[package]]
name = "writeable"
@@ -1894,7 +2382,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
"synstructure",
]
@@ -1915,7 +2403,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
"synstructure",
]
@@ -1955,14 +2443,14 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.119",
]
[[package]]
name = "zmij"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]]
name = "zstd"
diff --git a/lab_core/Cargo.toml b/lab_core/Cargo.toml
@@ -11,7 +11,7 @@ rustls-pemfile = "2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
log = "0.4"
-env_logger = "0.10"
+env_logger = "0.11"
rcgen = "0"
dotenv = "0"
base64 = "0"
diff --git a/lab_core/src/auth.rs b/lab_core/src/auth.rs
@@ -9,6 +9,7 @@ use std::env;
use std::future::{Ready, ready};
pub struct AuthenticatedDevice {
+ #[allow(dead_code)]
pub device_id: String,
}
diff --git a/lab_core/src/main.rs b/lab_core/src/main.rs
@@ -26,7 +26,7 @@ async fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok().content_type(ContentType::html()).body(
"<!DOCTYPE html><html><body>\
- <p>Welcome to your TLS-secured homepage!</p>\
+ <h1>Lab Presence</h1>\
</body></html>",
)
}
@@ -164,3 +164,88 @@ async fn put_presence(
HttpResponse::Ok().json(serde_json::json!({}))
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use actix_web::{App, test, web};
+ use shared::{LoginRequest, LoginResponse, PresenceUpdateRequest};
+
+ #[actix_web::test]
+ async fn test_auth_and_presence_flow() {
+ let state = web::Data::new(AppState {
+ presences: Mutex::new(HashMap::new()),
+ tokens: Mutex::new(HashMap::new()),
+ });
+
+ let app = test::init_service(
+ App::new()
+ .app_data(state.clone())
+ .service(login)
+ .service(get_presence)
+ .service(put_presence),
+ )
+ .await;
+
+ let invalid_login_req = test::TestRequest::post()
+ .uri("/api/v1/login")
+ .set_json(&LoginRequest {
+ device_id: "nfc_reader_01".to_string(),
+ device_secret: "wrong_secret".to_string(),
+ })
+ .to_request();
+
+ let resp = test::call_service(&app, invalid_login_req).await;
+ assert_eq!(resp.status(), 403);
+
+ let valid_login_req = test::TestRequest::post()
+ .uri("/api/v1/login")
+ .set_json(&LoginRequest {
+ device_id: "nfc".to_string(),
+ device_secret: "nfc".to_string(),
+ })
+ .to_request();
+
+ let resp = test::call_service(&app, valid_login_req).await;
+ assert_eq!(resp.status(), 200);
+
+ let login_res: LoginResponse = test::read_body_json(resp).await;
+ let token = login_res.access_token;
+ assert!(!token.is_empty());
+
+ let unauth_put_req = test::TestRequest::put()
+ .uri("/api/v1/presence/user_hash_123/status")
+ .set_json(&PresenceUpdateRequest {
+ presence: "online".to_string(),
+ status_msg: "In Lab".to_string(),
+ })
+ .to_request();
+
+ let resp = test::call_service(&app, unauth_put_req).await;
+ assert_eq!(resp.status(), 403);
+
+ let auth_put_req = test::TestRequest::put()
+ .uri("/api/v1/presence/user_hash_123/status")
+ .insert_header(("Authorization", format!("Bearer {}", token)))
+ .set_json(&PresenceUpdateRequest {
+ presence: "online".to_string(),
+ status_msg: "In Lab".to_string(),
+ })
+ .to_request();
+
+ let resp = test::call_service(&app, auth_put_req).await;
+ assert_eq!(resp.status(), 200);
+
+ let get_req = test::TestRequest::get()
+ .uri("/api/v1/presence/user_hash_123/status")
+ .insert_header(("Authorization", format!("Bearer {}", token)))
+ .to_request();
+
+ let resp = test::call_service(&app, get_req).await;
+ assert_eq!(resp.status(), 200);
+
+ let presence_res: PresenceResponse = test::read_body_json(resp).await;
+ assert_eq!(presence_res.presence, "online");
+ assert_eq!(presence_res.status_msg, "In Lab");
+ }
+}
diff --git a/nfc_sender/Cargo.toml b/nfc_sender/Cargo.toml
@@ -4,3 +4,11 @@ version = "0.1.0"
edition = "2024"
[dependencies]
+shared = { path = "../shared" }
+
+ureq = { version = "3", features = ["json"] }
+pcsc = { version = "2" }
+dotenvy = { version = "0"}
+serde_json = { version = "1" }
+tokio = { version = "1", features = ["fs"] }
+wiremock = { version = "0.6.5" }
diff --git a/nfc_sender/src/main.rs b/nfc_sender/src/main.rs
@@ -1,3 +1,234 @@
-fn main() {
- println!("Hello, world!");
+use pcsc::{Context, Protocols, Scope, ShareMode};
+use shared::{MemberDirectory, UserId};
+use std::thread::sleep;
+use std::time::Duration;
+
+struct NfcSender {
+ core_url: String,
+ token: String,
+ member_directory: MemberDirectory,
+}
+
+impl NfcSender {
+ fn new() -> Self {
+ Self {
+ core_url: String::new(),
+ token: String::new(),
+ member_directory: MemberDirectory::new(vec![]),
+ }
+ }
+ fn init(&mut self) -> Result<(), Box<dyn std::error::Error>> {
+ self.core_url =
+ std::env::var("CORE_URL").unwrap_or_else(|_| "https://localhost:8443".to_string());
+ self.member_directory = serde_json::from_str(&std::env::var("MEMBER_DIRECTORY")?)?;
+ let allowed_devices: serde_json::Value =
+ serde_json::from_str(&std::env::var("ALLOWED_DEVICES")?)?;
+ let secret: &str = allowed_devices["nfc"]
+ .as_str()
+ .ok_or("ALLOWED_DEVICES.nfc is not a string or missing")?;
+ self.login(secret.to_string())?;
+ Ok(())
+ }
+
+ fn login(&mut self, secret: String) -> Result<(), Box<dyn std::error::Error>> {
+ let login_request = shared::LoginRequest {
+ device_id: "nfc".to_string(),
+ device_secret: secret,
+ };
+ let response: shared::LoginResponse =
+ ureq::post(&format!("{}/api/v1/login", self.core_url))
+ .send_json(login_request)?
+ .body_mut()
+ .read_json::<shared::LoginResponse>()?;
+ self.token = response.access_token;
+ Ok(())
+ }
+ fn update_presence(&self, user_id: &UserId, presence: &str) -> Result<(), ureq::Error> {
+ let request_body = shared::PresenceUpdateRequest {
+ presence: presence.to_string(),
+ status_msg: "NFC toggle request sent".to_string(),
+ };
+ let _ = ureq::put(&format!(
+ "{}/api/v1/presence/{}/status",
+ self.core_url, user_id.id
+ ))
+ .header("Authorization", &format!("Bearer {}", self.token))
+ .send_json(request_body)?;
+ Ok(())
+ }
+ fn get_presence(&self, user_id: &UserId) -> Result<String, ureq::Error> {
+ let now_presence_res = ureq::get(&format!(
+ "{}/api/v1/presence/{}/status",
+ self.core_url, user_id.id
+ ))
+ .header("Authorization", &format!("Bearer {}", self.token))
+ .call()?
+ .body_mut()
+ .read_json::<shared::PresenceResponse>()?;
+ Ok(now_presence_res.presence)
+ }
+ fn toggled_presence(presence: &str) -> &'static str {
+ if presence == "inLab" {
+ "offline"
+ } else {
+ "inLab"
+ }
+ }
+}
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let _ = dotenvy::dotenv();
+ let ctx = Context::establish(Scope::User)?;
+
+ let mut nfc_sender: NfcSender = NfcSender::new();
+ nfc_sender.init()?;
+
+ println!("NFC sender has started...");
+
+ loop {
+ let mut readers_buf = [0u8; 2048];
+ let mut readers = match ctx.list_readers(&mut readers_buf) {
+ Ok(r) => r,
+ Err(_) => {
+ sleep(Duration::from_secs(1));
+ continue;
+ }
+ };
+
+ let reader_name = match readers.next() {
+ Some(name) => name,
+ None => {
+ sleep(Duration::from_secs(1));
+ continue;
+ }
+ };
+
+ if let Ok(card) = ctx.connect(reader_name, ShareMode::Shared, Protocols::ANY) {
+ let felica_poll_cmd = [
+ 0xFF, 0x00, 0x00, 0x00, 0x08, 0xD4, 0x40, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00,
+ ];
+ let mut rapdu_buf = [0u8; 258];
+
+ if let Ok(rapdu) = card.transmit(&felica_poll_cmd, &mut rapdu_buf) {
+ if rapdu.len() >= 18 {
+ let idm_bytes = &rapdu[10..18];
+ let idm_str = idm_bytes
+ .iter()
+ .map(|b| format!("{:02X}", b))
+ .collect::<String>();
+
+ if let Ok(user_id) = nfc_sender.member_directory.inquire(&idm_str) {
+ if let Ok(current_presence) = nfc_sender.get_presence(&user_id) {
+ let toggled = NfcSender::toggled_presence(¤t_presence);
+ let _ = nfc_sender.update_presence(&user_id, toggled);
+ }
+ }
+
+ sleep(Duration::from_secs(2));
+ }
+ }
+ }
+
+ sleep(Duration::from_millis(500));
+ }
+}
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn extract_idm(rapdu: &[u8]) -> Option<String> {
+ if rapdu.len() < 18 {
+ return None;
+ }
+ Some(
+ rapdu[10..18]
+ .iter()
+ .map(|b| format!("{:02X}", b))
+ .collect::<String>(),
+ )
+ }
+
+ #[test]
+ fn test_toggled_presence_in_lab_to_offline() {
+ let current = "inLab";
+ let next = NfcSender::toggled_presence(current);
+ assert_eq!(next, "offline");
+ }
+
+ #[test]
+ fn test_toggled_presence_offline_to_in_lab() {
+ let current = "offline";
+ let next = NfcSender::toggled_presence(current);
+ assert_eq!(next, "inLab");
+ }
+
+ #[test]
+ fn test_toggled_presence_unknown_status_defaults_to_in_lab() {
+ let current = "unknown";
+ let next = NfcSender::toggled_presence(current);
+ assert_eq!(next, "inLab");
+ }
+
+ #[test]
+ fn test_extract_idm_valid_rapdu() {
+ let mut rapdu = vec![0u8; 18];
+ rapdu[10..18].copy_from_slice(&[0x01, 0x2E, 0x3F, 0x4A, 0x5B, 0x6C, 0x7D, 0x8E]);
+
+ let idm = extract_idm(&rapdu);
+ assert_eq!(idm, Some("012E3F4A5B6C7D8E".to_string()));
+ }
+
+ #[test]
+ fn test_extract_idm_short_rapdu_returns_none() {
+ let rapdu = vec![0u8; 17];
+ let idm = extract_idm(&rapdu);
+ assert_eq!(idm, None);
+ }
+}
+#[cfg(test)]
+mod integration_tests {
+ use super::*;
+ use wiremock::matchers::{header, method, path};
+ use wiremock::{Mock, MockServer, ResponseTemplate};
+
+ #[tokio::test]
+ async fn test_nfc_sender_api_flow() -> Result<(), Box<dyn std::error::Error>> {
+ let mock_server = MockServer::start().await;
+
+ Mock::given(method("GET"))
+ .and(path("/api/v1/presence/user_123/status"))
+ .and(header("Authorization", "Bearer mock_token"))
+ .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
+ "presence": "inLab",
+ "status_msg": "NFC toggle request sent",
+ "timestamp": 1625097600
+ })))
+ .mount(&mock_server)
+ .await;
+
+ Mock::given(method("PUT"))
+ .and(path("/api/v1/presence/user_123/status"))
+ .and(header("Authorization", "Bearer mock_token"))
+ .respond_with(ResponseTemplate::new(200))
+ .mount(&mock_server)
+ .await;
+
+ let sender = NfcSender {
+ core_url: mock_server.uri(),
+ token: "mock_token".to_string(),
+ member_directory: MemberDirectory::new(vec![]),
+ };
+
+ let dummy_user = UserId {
+ id: "user_123".to_string(),
+ };
+
+ let presence = sender.get_presence(&dummy_user)?;
+ assert_eq!(presence, "inLab");
+
+ let toggled = NfcSender::toggled_presence(&presence);
+ let update_res = sender.update_presence(&dummy_user, toggled);
+ assert!(update_res.is_ok());
+
+ Ok(())
+ }
}
diff --git a/shared/src/lib.rs b/shared/src/lib.rs
@@ -1,6 +1,4 @@
-use dotenv::dotenv;
use serde::{Deserialize, Serialize};
-use sha2::Digest;
use std::error::Error;
#[derive(Serialize, Deserialize, Clone)]
@@ -63,38 +61,52 @@ impl LabPresence {
}
}
+#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserId {
pub id: String,
}
-impl UserId {
- pub fn get_id(idm: String) -> Result<Self, Box<dyn std::error::Error>> {
- let _ = dotenv();
- let salt = std::env::var("SALT")?;
- let mut hasher = sha2::Sha256::new();
- hasher.update(idm.as_bytes());
- hasher.update(salt.as_bytes());
- let result = hasher
- .finalize()
- .iter()
- .map(|b| format!("{:02x}", b))
- .collect::<String>();
- Ok(Self { id: result })
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct User {
+ pub id: UserId,
+ pub card_serial_number: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct MemberDirectory {
+ pub users: Vec<User>,
+}
+impl MemberDirectory {
+ pub fn new(users: Vec<User>) -> Self {
+ Self { users }
+ }
+ pub fn init_from_env() -> Result<Self, Box<dyn std::error::Error>> {
+ let member_directory = std::env::var("MEMBER_DIRECTORY")?;
+ let users: Vec<User> = serde_json::from_str(&member_directory)?;
+ Ok(Self::new(users))
+ }
+ pub fn inquire(&self, card_serial_number: &str) -> Result<UserId, Box<dyn std::error::Error>> {
+ self.users
+ .iter()
+ .find(|u| u.card_serial_number == card_serial_number)
+ .map(|u| u.id.clone())
+ .ok_or_else(|| "User not found".into())
}
}
-#[derive(Deserialize)]
+
+#[derive(Serialize, Deserialize)]
pub struct LoginRequest {
pub device_id: String,
pub device_secret: String,
}
-#[derive(Serialize)]
+#[derive(Serialize, Deserialize)]
pub struct LoginResponse {
pub access_token: String,
pub expires_in: u32,
}
-#[derive(Serialize)]
+#[derive(Serialize, Deserialize)]
pub struct PresenceResponse {
pub presence: String,
pub status_msg: String,
@@ -107,7 +119,7 @@ pub struct PresenceUpdateRequest {
pub status_msg: String,
}
-#[derive(Serialize)]
+#[derive(Serialize, Deserialize)]
pub struct ErrorResponse {
pub errcode: String,
pub error: String,