added signatures
This commit is contained in:
		
							parent
							
								
									4ebb2a21d2
								
							
						
					
					
						commit
						47af1e62b7
					
				
							
								
								
									
										1
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -7,6 +7,7 @@
 | 
				
			|||||||
        "Conn",
 | 
					        "Conn",
 | 
				
			||||||
        "dotenv",
 | 
					        "dotenv",
 | 
				
			||||||
        "hmac",
 | 
					        "hmac",
 | 
				
			||||||
 | 
					        "minisign",
 | 
				
			||||||
        "oneshot",
 | 
					        "oneshot",
 | 
				
			||||||
        "openapi",
 | 
					        "openapi",
 | 
				
			||||||
        "recv",
 | 
					        "recv",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										120
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										120
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -215,10 +215,12 @@ dependencies = [
 | 
				
			|||||||
 "error-stack",
 | 
					 "error-stack",
 | 
				
			||||||
 "hmac",
 | 
					 "hmac",
 | 
				
			||||||
 "ldap3",
 | 
					 "ldap3",
 | 
				
			||||||
 | 
					 "minisign",
 | 
				
			||||||
 "once_cell",
 | 
					 "once_cell",
 | 
				
			||||||
 "rand",
 | 
					 "rand",
 | 
				
			||||||
 "rust-argon2",
 | 
					 "rust-argon2",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_json",
 | 
				
			||||||
 "sha2",
 | 
					 "sha2",
 | 
				
			||||||
 "sqlx",
 | 
					 "sqlx",
 | 
				
			||||||
 "strum",
 | 
					 "strum",
 | 
				
			||||||
@ -396,6 +398,16 @@ dependencies = [
 | 
				
			|||||||
 "windows-targets 0.52.6",
 | 
					 "windows-targets 0.52.6",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cipher"
 | 
				
			||||||
 | 
					version = "0.4.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "crypto-common",
 | 
				
			||||||
 | 
					 "inout",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "clap"
 | 
					name = "clap"
 | 
				
			||||||
version = "4.5.23"
 | 
					version = "4.5.23"
 | 
				
			||||||
@ -600,6 +612,12 @@ dependencies = [
 | 
				
			|||||||
 "typenum",
 | 
					 "typenum",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "ct-codecs"
 | 
				
			||||||
 | 
					version = "1.1.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b916ba8ce9e4182696896f015e8a5ae6081b305f74690baa8465e35f5a142ea4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "der"
 | 
					name = "der"
 | 
				
			||||||
version = "0.7.9"
 | 
					version = "0.7.9"
 | 
				
			||||||
@ -1264,6 +1282,15 @@ dependencies = [
 | 
				
			|||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "inout"
 | 
				
			||||||
 | 
					version = "0.1.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "generic-array",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "is_terminal_polyfill"
 | 
					name = "is_terminal_polyfill"
 | 
				
			||||||
version = "1.70.1"
 | 
					version = "1.70.1"
 | 
				
			||||||
@ -1464,6 +1491,18 @@ version = "0.2.1"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 | 
					checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "minisign"
 | 
				
			||||||
 | 
					version = "0.7.9"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "26541387415a1e829df5d532aad019fb11bc723e2b5bc99edefa4cf5bfad0de7"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "ct-codecs",
 | 
				
			||||||
 | 
					 "getrandom",
 | 
				
			||||||
 | 
					 "rpassword",
 | 
				
			||||||
 | 
					 "scrypt",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "miniz_oxide"
 | 
					name = "miniz_oxide"
 | 
				
			||||||
version = "0.8.2"
 | 
					version = "0.8.2"
 | 
				
			||||||
@ -1709,6 +1748,16 @@ version = "0.2.3"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
 | 
					checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "pbkdf2"
 | 
				
			||||||
 | 
					version = "0.12.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "digest",
 | 
				
			||||||
 | 
					 "hmac",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pem"
 | 
					name = "pem"
 | 
				
			||||||
version = "3.0.4"
 | 
					version = "3.0.4"
 | 
				
			||||||
@ -1961,6 +2010,17 @@ dependencies = [
 | 
				
			|||||||
 "serde_derive",
 | 
					 "serde_derive",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rpassword"
 | 
				
			||||||
 | 
					version = "7.3.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "rtoolbox",
 | 
				
			||||||
 | 
					 "windows-sys 0.48.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rsa"
 | 
					name = "rsa"
 | 
				
			||||||
version = "0.9.7"
 | 
					version = "0.9.7"
 | 
				
			||||||
@ -1981,6 +2041,16 @@ dependencies = [
 | 
				
			|||||||
 "zeroize",
 | 
					 "zeroize",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rtoolbox"
 | 
				
			||||||
 | 
					version = "0.0.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "windows-sys 0.48.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rust-argon2"
 | 
					name = "rust-argon2"
 | 
				
			||||||
version = "2.1.0"
 | 
					version = "2.1.0"
 | 
				
			||||||
@ -2077,6 +2147,15 @@ version = "1.0.18"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 | 
					checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "salsa20"
 | 
				
			||||||
 | 
					version = "0.10.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "cipher",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "same-file"
 | 
					name = "same-file"
 | 
				
			||||||
version = "1.0.6"
 | 
					version = "1.0.6"
 | 
				
			||||||
@ -2101,6 +2180,17 @@ version = "1.2.0"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 | 
					checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "scrypt"
 | 
				
			||||||
 | 
					version = "0.11.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "pbkdf2",
 | 
				
			||||||
 | 
					 "salsa20",
 | 
				
			||||||
 | 
					 "sha2",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "security-framework"
 | 
					name = "security-framework"
 | 
				
			||||||
version = "2.11.1"
 | 
					version = "2.11.1"
 | 
				
			||||||
@ -3053,8 +3143,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa"
 | 
					name = "utoipa"
 | 
				
			||||||
version = "5.3.0"
 | 
					version = "5.3.1"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "indexmap",
 | 
					 "indexmap",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
@ -3064,8 +3155,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa-axum"
 | 
					name = "utoipa-axum"
 | 
				
			||||||
version = "0.1.3"
 | 
					version = "0.1.4"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ff0d605008ed085986e1803fd5c81d18c0f8503b1e4bbb21ea75b3fc20dd1efb"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "axum",
 | 
					 "axum",
 | 
				
			||||||
 "paste",
 | 
					 "paste",
 | 
				
			||||||
@ -3076,8 +3168,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa-gen"
 | 
					name = "utoipa-gen"
 | 
				
			||||||
version = "5.3.0"
 | 
					version = "5.3.1"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
@ -3087,8 +3180,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa-redoc"
 | 
					name = "utoipa-redoc"
 | 
				
			||||||
version = "5.0.0"
 | 
					version = "5.0.1"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "33749b636458b2ed3e8ebc633febffb3e4ed7298d9f749e9b71cc25ea2f0eb9f"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "axum",
 | 
					 "axum",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
@ -3098,8 +3192,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa-scalar"
 | 
					name = "utoipa-scalar"
 | 
				
			||||||
version = "0.2.0"
 | 
					version = "0.2.1"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "088e93bf19f6bd06e0aacb02ca432b3c5a449c4aec2e4aa9fc333a667f2b2c55"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "axum",
 | 
					 "axum",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
@ -3109,8 +3204,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "utoipa-swagger-ui"
 | 
					name = "utoipa-swagger-ui"
 | 
				
			||||||
version = "8.1.0"
 | 
					version = "8.1.1"
 | 
				
			||||||
source = "git+https://github.com/juhaku/utoipa#3ffad4bed73e5caeddc311bab70f810d1d772079"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "040cad8bd8de63f3d028e08e5b39be49d68f8a646e99f4aea2e2d4d82c34b21f"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "axum",
 | 
					 "axum",
 | 
				
			||||||
 "base64 0.22.1",
 | 
					 "base64 0.22.1",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										23
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -26,6 +26,7 @@ config = "0.15.4"
 | 
				
			|||||||
uuid = { version = "1.11.0", features = ["v4"] }
 | 
					uuid = { version = "1.11.0", features = ["v4"] }
 | 
				
			||||||
sha2 = "0.10.8"
 | 
					sha2 = "0.10.8"
 | 
				
			||||||
hmac = "0.12.1"
 | 
					hmac = "0.12.1"
 | 
				
			||||||
 | 
					minisign = "0.7.9"
 | 
				
			||||||
# axum-jwt-login = { path = "../axum-login-jwt" }
 | 
					# axum-jwt-login = { path = "../axum-login-jwt" }
 | 
				
			||||||
axum-jwt-login = { version = "0.1.0", registry = "kellnr" }
 | 
					axum-jwt-login = { version = "0.1.0", registry = "kellnr" }
 | 
				
			||||||
rust-argon2 = "2.1.0"
 | 
					rust-argon2 = "2.1.0"
 | 
				
			||||||
@ -38,27 +39,17 @@ windows-service = "0.7.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
axum = { version = "0.8.1", features = ["macros"] }
 | 
					axum = { version = "0.8.1", features = ["macros"] }
 | 
				
			||||||
strum = { version = "0.26", features = ["derive"] }
 | 
					strum = { version = "0.26", features = ["derive"] }
 | 
				
			||||||
# utoipa = { version = "5.3.0", features = ["axum_extras"] }
 | 
					utoipa = { version = "5.3.1", features = ["axum_extras"] }
 | 
				
			||||||
utoipa = { git = "https://github.com/juhaku/utoipa", features = [
 | 
					utoipa-axum = "0.1.4"
 | 
				
			||||||
    "axum_extras",
 | 
					utoipa-swagger-ui = { version = "8.1.1", features = ["axum"] }
 | 
				
			||||||
] }
 | 
					utoipa-redoc = { version = "*", features = ["axum"] }
 | 
				
			||||||
# utoipa-axum = "0.1.3"
 | 
					utoipa-scalar = { version = "*", features = ["axum"] }
 | 
				
			||||||
utoipa-axum = { git = "https://github.com/juhaku/utoipa" }
 | 
					 | 
				
			||||||
# utoipa-swagger-ui = { version = "8.1.0", features = ["axum"] }
 | 
					 | 
				
			||||||
# utoipa-redoc = { version = "*", features = ["axum"] }
 | 
					 | 
				
			||||||
# utoipa-scalar = { version = "*", features = ["axum"] }
 | 
					 | 
				
			||||||
utoipa-swagger-ui = { git = "https://github.com/juhaku/utoipa", features = [
 | 
					 | 
				
			||||||
    "axum",
 | 
					 | 
				
			||||||
] }
 | 
					 | 
				
			||||||
utoipa-redoc = { git = "https://github.com/juhaku/utoipa", features = ["axum"] }
 | 
					 | 
				
			||||||
utoipa-scalar = { git = "https://github.com/juhaku/utoipa", features = [
 | 
					 | 
				
			||||||
    "axum",
 | 
					 | 
				
			||||||
] }
 | 
					 | 
				
			||||||
ts-rs = { version = "10.1.0", features = ["chrono-impl"] }
 | 
					ts-rs = { version = "10.1.0", features = ["chrono-impl"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Utilities
 | 
					# Utilities
 | 
				
			||||||
# ========================================
 | 
					# ========================================
 | 
				
			||||||
serde = { version = "1.0.216", features = ["derive"] }
 | 
					serde = { version = "1.0.216", features = ["derive"] }
 | 
				
			||||||
 | 
					serde_json = "1.0.134"
 | 
				
			||||||
tokio = { version = "1.42.0", features = ["full"] }
 | 
					tokio = { version = "1.42.0", features = ["full"] }
 | 
				
			||||||
tokio-util = { version = "0.7.13", features = ["rt"] }
 | 
					tokio-util = { version = "0.7.13", features = ["rt"] }
 | 
				
			||||||
once_cell = "1.20.2"
 | 
					once_cell = "1.20.2"
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ pub const AUTH_TAG: &str = "Authentication";
 | 
				
			|||||||
pub const USERS_TAG: &str = "Users";
 | 
					pub const USERS_TAG: &str = "Users";
 | 
				
			||||||
pub const ORDER_TAG: &str = "order";
 | 
					pub const ORDER_TAG: &str = "order";
 | 
				
			||||||
pub const API_KEY_TAG: &str = "API Keys";
 | 
					pub const API_KEY_TAG: &str = "API Keys";
 | 
				
			||||||
 | 
					pub const SIGNATURE_TAG: &str = "Signature";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(OpenApi)]
 | 
					#[derive(OpenApi)]
 | 
				
			||||||
#[openapi(
 | 
					#[openapi(
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
pub mod api_keys;
 | 
					pub mod api_keys;
 | 
				
			||||||
pub mod auth;
 | 
					pub mod auth;
 | 
				
			||||||
 | 
					mod signature;
 | 
				
			||||||
pub mod users;
 | 
					pub mod users;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use api_keys::models::ApiKey as APIKey;
 | 
					use api_keys::models::ApiKey as APIKey;
 | 
				
			||||||
@ -27,8 +28,8 @@ macro_rules! login_required {
 | 
				
			|||||||
macro_rules! permission_required {
 | 
					macro_rules! permission_required {
 | 
				
			||||||
    ($($perm:expr),+ $(,)?) => {
 | 
					    ($($perm:expr),+ $(,)?) => {
 | 
				
			||||||
        axum_jwt_login::permission_required!(
 | 
					        axum_jwt_login::permission_required!(
 | 
				
			||||||
            User,
 | 
					            crate::api::routes::User,
 | 
				
			||||||
            ApiBackend,
 | 
					            crate::api::ApiBackend,
 | 
				
			||||||
            crate::api::routes::APIKey,
 | 
					            crate::api::routes::APIKey,
 | 
				
			||||||
            $($perm),+
 | 
					            $($perm),+
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@ -42,6 +43,7 @@ pub fn create_routes(session: AuthBackendType) -> Router {
 | 
				
			|||||||
        .nest(API_BASE, auth::router())
 | 
					        .nest(API_BASE, auth::router())
 | 
				
			||||||
        .nest(API_BASE, users::router())
 | 
					        .nest(API_BASE, users::router())
 | 
				
			||||||
        .nest(API_BASE, api_keys::router())
 | 
					        .nest(API_BASE, api_keys::router())
 | 
				
			||||||
 | 
					        .nest(API_BASE, signature::router())
 | 
				
			||||||
        //         .nest(
 | 
					        //         .nest(
 | 
				
			||||||
        //             "/api/order",
 | 
					        //             "/api/order",
 | 
				
			||||||
        //             // order::router().route_layer(crate::login_required!(AuthenticationBackend<ApiKey>)),
 | 
					        //             // order::router().route_layer(crate::login_required!(AuthenticationBackend<ApiKey>)),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										275
									
								
								src/api/routes/signature/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								src/api/routes/signature/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,275 @@
 | 
				
			|||||||
 | 
					use std::io::Cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use axum::{debug_handler, Json};
 | 
				
			||||||
 | 
					use chrono::Utc;
 | 
				
			||||||
 | 
					use minisign::{KeyPair, PublicKeyBox, SecretKeyBox, SignatureBox};
 | 
				
			||||||
 | 
					use models::{
 | 
				
			||||||
 | 
					    PublicKeyID, SignatureTest, SigningKeyRequest, SigningKeyStatus, VerificationRequest,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use utoipa_axum::{router::OpenApiRouter, routes};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    api::{
 | 
				
			||||||
 | 
					        description::SIGNATURE_TAG,
 | 
				
			||||||
 | 
					        routes::users::permissions::{Permission, PermissionDetail},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    errors::ApiError,
 | 
				
			||||||
 | 
					    permission_required,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::AuthBackendType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod models;
 | 
				
			||||||
 | 
					pub mod sql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// expose the OpenAPI to parent module
 | 
				
			||||||
 | 
					pub fn router() -> OpenApiRouter {
 | 
				
			||||||
 | 
					    let verify = OpenApiRouter::new().routes(routes!(verify));
 | 
				
			||||||
 | 
					    let write = OpenApiRouter::new()
 | 
				
			||||||
 | 
					        .routes(routes!(
 | 
				
			||||||
 | 
					            check_signing_key,
 | 
				
			||||||
 | 
					            create_signing_key,
 | 
				
			||||||
 | 
					            verify_signing_key
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        .routes(routes!(sign))
 | 
				
			||||||
 | 
					        .route_layer(permission_required!(Permission::Write(
 | 
				
			||||||
 | 
					            PermissionDetail::Signature
 | 
				
			||||||
 | 
					        )));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OpenApiRouter::new().merge(write).merge(verify)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[debug_handler]
 | 
				
			||||||
 | 
					#[utoipa::path(
 | 
				
			||||||
 | 
					    get,
 | 
				
			||||||
 | 
					    path = "/signature/key",
 | 
				
			||||||
 | 
					    summary = "Check for signing key",
 | 
				
			||||||
 | 
					    description = "Check if the logged in user has configured a signing key.",
 | 
				
			||||||
 | 
					    responses(
 | 
				
			||||||
 | 
					        (status = OK, body = SigningKeyStatus, description = "Signature Status"),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    security(
 | 
				
			||||||
 | 
					        ("user_auth" = ["write:signature",]),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    tag = SIGNATURE_TAG)]
 | 
				
			||||||
 | 
					pub async fn check_signing_key(
 | 
				
			||||||
 | 
					    auth_session: AuthBackendType,
 | 
				
			||||||
 | 
					) -> Result<Json<SigningKeyStatus>, ApiError> {
 | 
				
			||||||
 | 
					    let backend = auth_session.backend();
 | 
				
			||||||
 | 
					    let user = auth_session
 | 
				
			||||||
 | 
					        .is_authenticated()
 | 
				
			||||||
 | 
					        .ok_or(ApiError::AccessDenied)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let private_key = sql::get_private_key(backend.pool(), &user.user_id).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(Json(SigningKeyStatus {
 | 
				
			||||||
 | 
					        configured: private_key.key.is_some(),
 | 
				
			||||||
 | 
					        verified: None,
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[debug_handler]
 | 
				
			||||||
 | 
					#[utoipa::path(
 | 
				
			||||||
 | 
					    post,
 | 
				
			||||||
 | 
					    path = "/signature/key",
 | 
				
			||||||
 | 
					    summary = "Create signing key",
 | 
				
			||||||
 | 
					    description = "Create signing key for the currently logged in user.",
 | 
				
			||||||
 | 
					    request_body(content = SigningKeyRequest, description = "Signing Key Request", content_type = "application/json"),
 | 
				
			||||||
 | 
					    responses(
 | 
				
			||||||
 | 
					        (status = OK, description = "Successfully created signing key"),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    security(
 | 
				
			||||||
 | 
					        ("user_auth" = ["write:signature",]),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    tag = SIGNATURE_TAG)]
 | 
				
			||||||
 | 
					pub async fn create_signing_key(
 | 
				
			||||||
 | 
					    auth_session: AuthBackendType,
 | 
				
			||||||
 | 
					    Json(key_request): Json<SigningKeyRequest>,
 | 
				
			||||||
 | 
					) -> Result<(), ApiError> {
 | 
				
			||||||
 | 
					    let backend = auth_session.backend();
 | 
				
			||||||
 | 
					    let user = auth_session
 | 
				
			||||||
 | 
					        .is_authenticated()
 | 
				
			||||||
 | 
					        .ok_or(ApiError::AccessDenied)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create Key Pair
 | 
				
			||||||
 | 
					    let KeyPair { pk, sk } = KeyPair::generate_encrypted_keypair(Some(key_request.secret))?;
 | 
				
			||||||
 | 
					    let public_key = pk.to_box()?.to_string();
 | 
				
			||||||
 | 
					    let private_key = sk
 | 
				
			||||||
 | 
					        .to_box(Some(&format!(
 | 
				
			||||||
 | 
					            "{} {} - {}",
 | 
				
			||||||
 | 
					            user.name,
 | 
				
			||||||
 | 
					            user.surname,
 | 
				
			||||||
 | 
					            Utc::now()
 | 
				
			||||||
 | 
					        )))? // Optional comment about the key
 | 
				
			||||||
 | 
					        .to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // store keys in database
 | 
				
			||||||
 | 
					    sql::update_private_key(backend.pool(), &user.user_id, private_key).await?;
 | 
				
			||||||
 | 
					    sql::store_public_key(backend.pool(), &user.user_id, public_key).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[debug_handler]
 | 
				
			||||||
 | 
					#[utoipa::path(
 | 
				
			||||||
 | 
					    put,
 | 
				
			||||||
 | 
					    path = "/signature/key",
 | 
				
			||||||
 | 
					    summary = "Verify signing key",
 | 
				
			||||||
 | 
					    description = "Verify signing key for the currently logged in user.",
 | 
				
			||||||
 | 
					    request_body(content = SigningKeyRequest, description = "Signing Key Request", content_type = "application/json"),
 | 
				
			||||||
 | 
					    responses(
 | 
				
			||||||
 | 
					        (status = OK, body = SigningKeyStatus, description = "Signature Status"),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    security(
 | 
				
			||||||
 | 
					        ("user_auth" = ["write:signature",]),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    tag = SIGNATURE_TAG)]
 | 
				
			||||||
 | 
					pub async fn verify_signing_key(
 | 
				
			||||||
 | 
					    auth_session: AuthBackendType,
 | 
				
			||||||
 | 
					    Json(key_request): Json<SigningKeyRequest>,
 | 
				
			||||||
 | 
					) -> Result<Json<SigningKeyStatus>, ApiError> {
 | 
				
			||||||
 | 
					    let backend = auth_session.backend();
 | 
				
			||||||
 | 
					    let user = auth_session
 | 
				
			||||||
 | 
					        .is_authenticated()
 | 
				
			||||||
 | 
					        .ok_or(ApiError::AccessDenied)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get private key
 | 
				
			||||||
 | 
					    let status = match key_request
 | 
				
			||||||
 | 
					        .to_private_key(backend.pool(), &user.user_id)
 | 
				
			||||||
 | 
					        .await?
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Some(_key) => SigningKeyStatus {
 | 
				
			||||||
 | 
					            configured: true,
 | 
				
			||||||
 | 
					            verified: Some(true),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        None => SigningKeyStatus {
 | 
				
			||||||
 | 
					            configured: false,
 | 
				
			||||||
 | 
					            verified: None,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(Json(status))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[debug_handler]
 | 
				
			||||||
 | 
					#[utoipa::path(
 | 
				
			||||||
 | 
					    post,
 | 
				
			||||||
 | 
					    path = "/signature/sign",
 | 
				
			||||||
 | 
					    summary = "Sign something with your signing key",
 | 
				
			||||||
 | 
					    description = "Sign something as the currently logged in user.",
 | 
				
			||||||
 | 
					    request_body(content = SigningKeyRequest, description = "Signing Key Request", content_type = "application/json"),
 | 
				
			||||||
 | 
					    responses(
 | 
				
			||||||
 | 
					        (status = OK, description = "Successfully signed"),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    security(
 | 
				
			||||||
 | 
					        ("user_auth" = ["write:signature",]),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    tag = SIGNATURE_TAG)]
 | 
				
			||||||
 | 
					pub async fn sign(
 | 
				
			||||||
 | 
					    auth_session: AuthBackendType,
 | 
				
			||||||
 | 
					    Json(key_request): Json<SigningKeyRequest>,
 | 
				
			||||||
 | 
					) -> Result<Json<VerificationRequest>, ApiError> {
 | 
				
			||||||
 | 
					    let backend = auth_session.backend();
 | 
				
			||||||
 | 
					    let user = auth_session
 | 
				
			||||||
 | 
					        .is_authenticated()
 | 
				
			||||||
 | 
					        .ok_or(ApiError::AccessDenied)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get private key
 | 
				
			||||||
 | 
					    let private_key = key_request
 | 
				
			||||||
 | 
					        .to_private_key(backend.pool(), &user.user_id)
 | 
				
			||||||
 | 
					        .await?
 | 
				
			||||||
 | 
					        .ok_or(ApiError::AccessDenied)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get current public key
 | 
				
			||||||
 | 
					    let public_key_id = sql::get_current_public_key(backend.pool(), &user.user_id)
 | 
				
			||||||
 | 
					        .await?
 | 
				
			||||||
 | 
					        .ok_or(ApiError::InternalError(
 | 
				
			||||||
 | 
					            "No corresponding public key found".to_string(),
 | 
				
			||||||
 | 
					        ))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let data = SignatureTest {
 | 
				
			||||||
 | 
					        value1: "blfjdljfa".to_string(),
 | 
				
			||||||
 | 
					        value2: false,
 | 
				
			||||||
 | 
					        value3: vec!["bladfa".to_string(), "noch bla".to_string()],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let data_reader = Cursor::new(data.calculate_hash().to_be_bytes());
 | 
				
			||||||
 | 
					    let signature_box = minisign::sign(
 | 
				
			||||||
 | 
					        None,
 | 
				
			||||||
 | 
					        &private_key,
 | 
				
			||||||
 | 
					        data_reader,
 | 
				
			||||||
 | 
					        None,
 | 
				
			||||||
 | 
					        Some(&serde_json::to_string(&public_key_id)?),
 | 
				
			||||||
 | 
					    )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We have a signature! Let's inspect it a little bit.
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "Untrusted comment: [{}]",
 | 
				
			||||||
 | 
					        signature_box.untrusted_comment().unwrap()
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    println!(
 | 
				
			||||||
 | 
					        "Trusted comment: [{}]",
 | 
				
			||||||
 | 
					        signature_box.trusted_comment().unwrap()
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // store signature
 | 
				
			||||||
 | 
					    // Converting the signature box to a string in order to save it is easy.
 | 
				
			||||||
 | 
					    let signature = signature_box.into_string();
 | 
				
			||||||
 | 
					    println!("BLA");
 | 
				
			||||||
 | 
					    println!("{signature}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(Json(VerificationRequest {
 | 
				
			||||||
 | 
					        signature,
 | 
				
			||||||
 | 
					        signed_from: "".to_string(),
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[debug_handler]
 | 
				
			||||||
 | 
					#[utoipa::path(
 | 
				
			||||||
 | 
					    get,
 | 
				
			||||||
 | 
					    path = "/signature/verify",
 | 
				
			||||||
 | 
					    summary = "Verify a signature",
 | 
				
			||||||
 | 
					    description = "Verify the signature.",
 | 
				
			||||||
 | 
					    request_body(content = SigningKeyRequest, description = "Signing Key Request", content_type = "application/json"),
 | 
				
			||||||
 | 
					    responses(
 | 
				
			||||||
 | 
					        (status = OK, description = "Successfully signed"),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    tag = SIGNATURE_TAG)]
 | 
				
			||||||
 | 
					pub async fn verify(
 | 
				
			||||||
 | 
					    auth_session: AuthBackendType,
 | 
				
			||||||
 | 
					    Json(verification_request): Json<VerificationRequest>,
 | 
				
			||||||
 | 
					) -> Result<(), ApiError> {
 | 
				
			||||||
 | 
					    let backend = auth_session.backend();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Now, let's verify the signature.
 | 
				
			||||||
 | 
					    // Assuming we just loaded it into `signature_box_str`, get the box back.
 | 
				
			||||||
 | 
					    let signature_box = SignatureBox::from_string(&verification_request.signature)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get public key id from signature
 | 
				
			||||||
 | 
					    let public_key_id: PublicKeyID = serde_json::from_str(&signature_box.untrusted_comment()?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Load the public key of the user that signed.
 | 
				
			||||||
 | 
					    let public_key_str = sql::get_public_key_by_id(backend.pool(), public_key_id)
 | 
				
			||||||
 | 
					        .await?
 | 
				
			||||||
 | 
					        .key
 | 
				
			||||||
 | 
					        .ok_or(ApiError::InternalError(
 | 
				
			||||||
 | 
					            "No corresponding Public Key was found.".to_string(),
 | 
				
			||||||
 | 
					        ))?;
 | 
				
			||||||
 | 
					    let public_key_box = PublicKeyBox::from_string(&public_key_str)?;
 | 
				
			||||||
 | 
					    let public_key = public_key_box.into_public_key()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // And verify the data.
 | 
				
			||||||
 | 
					    let data = SignatureTest {
 | 
				
			||||||
 | 
					        value1: "blfjdljfa".to_string(),
 | 
				
			||||||
 | 
					        value2: false,
 | 
				
			||||||
 | 
					        value3: vec!["bladfa".to_string(), "noch bla".to_string()],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let data_reader = Cursor::new(data.calculate_hash().to_be_bytes());
 | 
				
			||||||
 | 
					    let verified = minisign::verify(&public_key, &signature_box, data_reader, true, false, false);
 | 
				
			||||||
 | 
					    match verified {
 | 
				
			||||||
 | 
					        Ok(()) => println!("Success!"),
 | 
				
			||||||
 | 
					        Err(_) => println!("Verification failed"),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										82
									
								
								src/api/routes/signature/models.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/api/routes/signature/models.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    fmt::Display,
 | 
				
			||||||
 | 
					    hash::{DefaultHasher, Hash, Hasher},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use chrono::NaiveDateTime;
 | 
				
			||||||
 | 
					use minisign::{SecretKey, SecretKeyBox};
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use sqlx::PgPool;
 | 
				
			||||||
 | 
					use ts_rs::TS;
 | 
				
			||||||
 | 
					use utoipa::ToSchema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{api::routes::signature::sql, errors::ApiError};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, TS, ToSchema)]
 | 
				
			||||||
 | 
					#[ts(export)]
 | 
				
			||||||
 | 
					pub struct SigningKeyStatus {
 | 
				
			||||||
 | 
					    pub configured: bool,
 | 
				
			||||||
 | 
					    pub verified: Option<bool>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct KeyData {
 | 
				
			||||||
 | 
					    pub key: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct PublicKeyID {
 | 
				
			||||||
 | 
					    pub timestamp: NaiveDateTime,
 | 
				
			||||||
 | 
					    pub user_id: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Deserialize, TS, ToSchema)]
 | 
				
			||||||
 | 
					#[ts(export)]
 | 
				
			||||||
 | 
					pub struct SigningKeyRequest {
 | 
				
			||||||
 | 
					    pub secret: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SigningKeyRequest {
 | 
				
			||||||
 | 
					    pub async fn to_private_key(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        pool: &PgPool,
 | 
				
			||||||
 | 
					        user_id: &str,
 | 
				
			||||||
 | 
					    ) -> Result<Option<SecretKey>, ApiError> {
 | 
				
			||||||
 | 
					        // try to get private key from database
 | 
				
			||||||
 | 
					        let private_key = match sql::get_private_key(pool, user_id).await?.key {
 | 
				
			||||||
 | 
					            Some(key) => key,
 | 
				
			||||||
 | 
					            None => return Ok(None),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // create MinSign Box from key string
 | 
				
			||||||
 | 
					        let private_key_box = SecretKeyBox::from_string(&private_key)?;
 | 
				
			||||||
 | 
					        // and the box can be opened using the password to reveal the original secret key:
 | 
				
			||||||
 | 
					        let secret_key = private_key_box.into_secret_key(Some(self.secret))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Some(secret_key))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Deserialize, Serialize, TS, ToSchema)]
 | 
				
			||||||
 | 
					#[ts(export)]
 | 
				
			||||||
 | 
					pub struct VerificationRequest {
 | 
				
			||||||
 | 
					    pub signature: String,
 | 
				
			||||||
 | 
					    pub signed_from: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Hash)]
 | 
				
			||||||
 | 
					pub struct SignatureTest {
 | 
				
			||||||
 | 
					    pub value1: String,
 | 
				
			||||||
 | 
					    pub value2: bool,
 | 
				
			||||||
 | 
					    pub value3: Vec<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SignatureTest
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Self: Hash,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    pub fn calculate_hash(&self) -> u64 {
 | 
				
			||||||
 | 
					        let mut s = DefaultHasher::new();
 | 
				
			||||||
 | 
					        self.hash(&mut s);
 | 
				
			||||||
 | 
					        s.finish()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/api/routes/signature/sql.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/api/routes/signature/sql.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					use sqlx::PgPool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    api::routes::signature::models::{KeyData, PublicKeyID},
 | 
				
			||||||
 | 
					    errors::ApiError,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn get_private_key(pool: &PgPool, user_id: &str) -> Result<KeyData, ApiError> {
 | 
				
			||||||
 | 
					    Ok(sqlx::query_as!(
 | 
				
			||||||
 | 
					        KeyData,
 | 
				
			||||||
 | 
					        r#"SELECT
 | 
				
			||||||
 | 
					            USERS."PrivateKey" as key
 | 
				
			||||||
 | 
					        FROM
 | 
				
			||||||
 | 
					            users
 | 
				
			||||||
 | 
					        WHERE USERS."UserID" = $1"#,
 | 
				
			||||||
 | 
					        user_id
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_one(pool)
 | 
				
			||||||
 | 
					    .await?)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn update_private_key(
 | 
				
			||||||
 | 
					    pool: &PgPool,
 | 
				
			||||||
 | 
					    user_id: &str,
 | 
				
			||||||
 | 
					    private_key: String,
 | 
				
			||||||
 | 
					) -> Result<(), ApiError> {
 | 
				
			||||||
 | 
					    sqlx::query!(
 | 
				
			||||||
 | 
					        r#"UPDATE users SET
 | 
				
			||||||
 | 
					            "PrivateKey" = $2
 | 
				
			||||||
 | 
					        WHERE "UserID" = $1"#,
 | 
				
			||||||
 | 
					        user_id,
 | 
				
			||||||
 | 
					        private_key
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .execute(pool)
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn store_public_key(
 | 
				
			||||||
 | 
					    pool: &PgPool,
 | 
				
			||||||
 | 
					    user_id: &str,
 | 
				
			||||||
 | 
					    public_key: String,
 | 
				
			||||||
 | 
					) -> Result<(), ApiError> {
 | 
				
			||||||
 | 
					    sqlx::query!(
 | 
				
			||||||
 | 
					        r#"INSERT INTO "PublicKeys"
 | 
				
			||||||
 | 
					            ("UserID", "PublicKey")
 | 
				
			||||||
 | 
					            VALUES ($1, $2)"#,
 | 
				
			||||||
 | 
					        user_id,
 | 
				
			||||||
 | 
					        public_key
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .execute(pool)
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn get_current_public_key(
 | 
				
			||||||
 | 
					    pool: &PgPool,
 | 
				
			||||||
 | 
					    user_id: &str,
 | 
				
			||||||
 | 
					) -> Result<Option<PublicKeyID>, ApiError> {
 | 
				
			||||||
 | 
					    Ok(sqlx::query_as!(
 | 
				
			||||||
 | 
					        PublicKeyID,
 | 
				
			||||||
 | 
					        r#"SELECT 
 | 
				
			||||||
 | 
					            "PublicKeys"."Timestamp" as timestamp,
 | 
				
			||||||
 | 
					            "PublicKeys"."UserID" as user_id
 | 
				
			||||||
 | 
					        FROM 
 | 
				
			||||||
 | 
					            "PublicKeys"
 | 
				
			||||||
 | 
					        WHERE
 | 
				
			||||||
 | 
					            "PublicKeys"."UserID" = $1
 | 
				
			||||||
 | 
					        ORDER BY timestamp DESC"#,
 | 
				
			||||||
 | 
					        user_id,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_optional(pool)
 | 
				
			||||||
 | 
					    .await?)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn get_public_key_by_id(pool: &PgPool, key_id: PublicKeyID) -> Result<KeyData, ApiError> {
 | 
				
			||||||
 | 
					    Ok(sqlx::query_as!(
 | 
				
			||||||
 | 
					        KeyData,
 | 
				
			||||||
 | 
					        r#"SELECT 
 | 
				
			||||||
 | 
					            "PublicKeys"."PublicKey" as key
 | 
				
			||||||
 | 
					        FROM 
 | 
				
			||||||
 | 
					            "PublicKeys"
 | 
				
			||||||
 | 
					        WHERE
 | 
				
			||||||
 | 
					            "PublicKeys"."UserID" = $1
 | 
				
			||||||
 | 
					            AND "PublicKeys"."Timestamp" = $2"#,
 | 
				
			||||||
 | 
					        key_id.user_id,
 | 
				
			||||||
 | 
					        key_id.timestamp,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .fetch_one(pool)
 | 
				
			||||||
 | 
					    .await?)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -61,6 +61,7 @@ impl Display for Permission {
 | 
				
			|||||||
pub enum PermissionDetail {
 | 
					pub enum PermissionDetail {
 | 
				
			||||||
    Users,
 | 
					    Users,
 | 
				
			||||||
    APIKeys,
 | 
					    APIKeys,
 | 
				
			||||||
 | 
					    Signature,
 | 
				
			||||||
    #[default]
 | 
					    #[default]
 | 
				
			||||||
    None,
 | 
					    None,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ use axum::{
 | 
				
			|||||||
    Json,
 | 
					    Json,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use error_stack::Context;
 | 
					use error_stack::Context;
 | 
				
			||||||
 | 
					use minisign::PError;
 | 
				
			||||||
use serde::Serialize;
 | 
					use serde::Serialize;
 | 
				
			||||||
use sha2::digest::InvalidLength;
 | 
					use sha2::digest::InvalidLength;
 | 
				
			||||||
use tracing::error;
 | 
					use tracing::error;
 | 
				
			||||||
@ -110,6 +111,18 @@ impl From<InvalidLength> for ApiError {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<PError> for ApiError {
 | 
				
			||||||
 | 
					    fn from(value: PError) -> Self {
 | 
				
			||||||
 | 
					        Self::InternalError(format!("MiniSign Error: {value}"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<serde_json::Error> for ApiError {
 | 
				
			||||||
 | 
					    fn from(value: serde_json::Error) -> Self {
 | 
				
			||||||
 | 
					        Self::InternalError(format!("Error on (de)serialization: {value}"))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for ApiError {
 | 
					impl Display for ApiError {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        let error_info = self.as_error_info().1;
 | 
					        let error_info = self.as_error_info().1;
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ CREATE TABLE public.users
 | 
				
			|||||||
    "Surname" character varying(250) NOT NULL DEFAULT '',
 | 
					    "Surname" character varying(250) NOT NULL DEFAULT '',
 | 
				
			||||||
    "Email" character varying(500) NOT NULL DEFAULT '',
 | 
					    "Email" character varying(500) NOT NULL DEFAULT '',
 | 
				
			||||||
    "Password" character varying(255) NOT NULL DEFAULT '',
 | 
					    "Password" character varying(255) NOT NULL DEFAULT '',
 | 
				
			||||||
 | 
					    "PrivateKey" text COLLATE,
 | 
				
			||||||
    "CreationDate" timestamp without time zone NOT NULL DEFAULT NOW(),
 | 
					    "CreationDate" timestamp without time zone NOT NULL DEFAULT NOW(),
 | 
				
			||||||
    "LastChanged" timestamp without time zone NOT NULL DEFAULT NOW(),
 | 
					    "LastChanged" timestamp without time zone NOT NULL DEFAULT NOW(),
 | 
				
			||||||
    "StatusFlag" smallint NOT NULL,
 | 
					    "StatusFlag" smallint NOT NULL,
 | 
				
			||||||
@ -16,4 +17,7 @@ ALTER TABLE IF EXISTS public.users
 | 
				
			|||||||
    OWNER to postgres;
 | 
					    OWNER to postgres;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COMMENT ON TABLE public.users
 | 
					COMMENT ON TABLE public.users
 | 
				
			||||||
    IS 'Table containing user information';
 | 
					    IS 'Table containing user information';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMENT ON COLUMN public.users."PrivateKey"
 | 
				
			||||||
 | 
					    IS 'Private Key of the user with which the user can sign things';
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/migrations/06_publc_keys.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/migrations/06_publc_keys.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					CREATE TABLE IF NOT EXISTS public."PublicKeys"
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    "Timestamp" timestamp without time zone NOT NULL DEFAULT now(),
 | 
				
			||||||
 | 
					    "UserID" character varying COLLATE pg_catalog."default" NOT NULL,
 | 
				
			||||||
 | 
					    "PublicKey" text COLLATE pg_catalog."default" NOT NULL,
 | 
				
			||||||
 | 
					    CONSTRAINT "PublicKeys_pkey" PRIMARY KEY ("Timestamp", "UserID"),
 | 
				
			||||||
 | 
					    CONSTRAINT "PublicKeyUserID" FOREIGN KEY ("UserID")
 | 
				
			||||||
 | 
					        REFERENCES public.users ("UserID") MATCH SIMPLE
 | 
				
			||||||
 | 
					        ON UPDATE NO ACTION
 | 
				
			||||||
 | 
					        ON DELETE NO ACTION
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TABLESPACE pg_default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE IF EXISTS public."PublicKeys"
 | 
				
			||||||
 | 
					    OWNER to postgres;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMENT ON TABLE public."PublicKeys"
 | 
				
			||||||
 | 
					    IS 'Public Key Storage for Signatures';
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user