Commit 58912e4a authored by Szymon Zimnowoda's avatar Szymon Zimnowoda
Browse files

Merge branch 'sz/create_user_account_again' into 'dev'

refactored shared_plugins to shared_state

See merge request !409
parents 6645546e f5233ba6
Showing with 960 additions and 172 deletions
+960 -172
......@@ -79,7 +79,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -213,7 +213,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -285,6 +285,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "argon2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c"
dependencies = [
"base64ct",
"blake2",
"password-hash",
]
[[package]]
name = "ascii"
version = "0.9.3"
......@@ -299,7 +310,7 @@ checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -325,12 +336,27 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.2"
......@@ -542,7 +568,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -754,7 +780,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -765,7 +791,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -778,7 +804,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -789,6 +815,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
......@@ -952,7 +979,7 @@ checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -1215,6 +1242,12 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "indexmap"
version = "1.9.1"
......@@ -1297,10 +1330,13 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eabca5e0b4d0e98e7f2243fb5b7520b6af2b65d8f87bcc86f2c75185a6ff243"
dependencies = [
"async-trait",
"base64",
"email-encoding",
"email_address",
"fastrand",
"futures-io",
"futures-util",
"httpdate",
"idna",
"mime",
......@@ -1310,19 +1346,22 @@ dependencies = [
"rustls",
"rustls-pemfile",
"socket2",
"tokio",
"tokio-rustls",
"webpki-roots",
]
[[package]]
name = "libc"
version = "0.2.132"
version = "0.2.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "libpod"
version = "0.4.4"
dependencies = [
"argon2",
"chacha20poly1305",
"clap 4.0.26",
"criterion",
......@@ -1348,6 +1387,7 @@ dependencies = [
"reqwest",
"rusqlite",
"scheduled-thread-pool",
"secrecy",
"serde",
"serde_json",
"serde_path_to_error",
......@@ -1358,6 +1398,7 @@ dependencies = [
"tokio",
"tracing",
"tracing-subscriber",
"validator",
"zeroize",
]
......@@ -1475,7 +1516,7 @@ dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
......@@ -1624,7 +1665,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -1682,7 +1723,18 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
......@@ -1714,7 +1766,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -1817,7 +1869,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
"version_check",
]
......@@ -1834,18 +1886,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.43"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
......@@ -1900,9 +1952,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
......@@ -1981,7 +2033,7 @@ dependencies = [
"quote",
"refinery-core",
"regex",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2144,7 +2196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
......@@ -2172,6 +2224,16 @@ dependencies = [
"untrusted",
]
[[package]]
name = "secrecy"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e"
dependencies = [
"serde",
"zeroize",
]
[[package]]
name = "security-framework"
version = "2.7.0"
......@@ -2228,7 +2290,7 @@ checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2282,7 +2344,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2358,9 +2420,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "socket2"
version = "0.4.7"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
......@@ -2395,6 +2457,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
......@@ -2436,7 +2509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901a55b0a7a06ebc4a674dcca925170da8e613fa3b163a1df804ed10afb154d"
dependencies = [
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2447,7 +2520,7 @@ checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2476,7 +2549,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2533,34 +2606,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.21.0"
version = "1.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42"
checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.15",
]
[[package]]
......@@ -2634,7 +2705,7 @@ checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
]
[[package]]
......@@ -2753,6 +2824,48 @@ dependencies = [
"serde",
]
[[package]]
name = "validator"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ad5bf234c7d3ad1042e5252b7eddb2c4669ee23f32c7dd0e9b7705f07ef591"
dependencies = [
"idna",
"lazy_static",
"regex",
"serde",
"serde_derive",
"serde_json",
"url",
"validator_derive",
]
[[package]]
name = "validator_derive"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
dependencies = [
"if_chain",
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
"regex",
"syn 1.0.99",
"validator_types",
]
[[package]]
name = "validator_types"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
dependencies = [
"proc-macro2",
"syn 1.0.99",
]
[[package]]
name = "valuable"
version = "0.1.0"
......@@ -2825,7 +2938,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
"wasm-bindgen-shared",
]
......@@ -2859,7 +2972,7 @@ checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.99",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
......@@ -2936,43 +3049,109 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows_aarch64_msvc 0.36.1",
"windows_i686_gnu 0.36.1",
"windows_i686_msvc 0.36.1",
"windows_x86_64_gnu 0.36.1",
"windows_x86_64_msvc 0.36.1",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winreg"
version = "0.10.1"
......
......@@ -17,7 +17,7 @@ sha2 = "0.10.2"
serde = { version = "1.0.130" }
serde_json = "1.0.72"
serde_path_to_error = "0.1.5"
tokio = { version = "1.14.0", features = ["full"] }
tokio = { version = "1.28.0", features = ["full"] }
tracing = "0.1.36"
tracing-subscriber = { version = "0.3.15" }
futures = "0.3.24"
......@@ -15,6 +15,8 @@ lettre = { version = "0.10.0-rc.3", default-features = false, features = [
"builder",
"rustls-tls",
"smtp-transport",
"tokio1-rustls-tls",
"tokio1"
] }
libc = "0.2.108"
md5 = "0.7.0"
......@@ -54,6 +56,9 @@ futures = { workspace = true }
thiserror = "1.0.38"
mime = "0.3.16"
scheduled-thread-pool = "0.2.7"
secrecy = { version = "0.8.0", features = ["serde"] }
argon2 = "0.5.0"
validator = { version = "0.16.0", features = ["derive"] }
[dev-dependencies]
criterion = {version = "0.3.5", features = ["async_tokio"]}
......
-- PodUserAccount {
-- login_hash: String,
-- password_hash: String,
-- salt: String,
-- state: String,
-- code: String
-- }
-- login: String
INSERT INTO items(id, type, dateCreated, dateModified, dateServerModified, deleted) VALUES(
"d894f98c-20cc-4de1-a7dc-b48590d2cadd",
"ItemPropertySchema", 0, 0, 0, 0
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "d894f98c-20cc-4de1-a7dc-b48590d2cadd"),
"itemType", "PodUserAccount"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "d894f98c-20cc-4de1-a7dc-b48590d2cadd"),
"propertyName", "loginHash"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "d894f98c-20cc-4de1-a7dc-b48590d2cadd"),
"valueType", "Text"
);
-- passwordHash: String
INSERT INTO items(id, type, dateCreated, dateModified, dateServerModified, deleted) VALUES(
"c9eff54e-8164-4b21-a00e-4c4e9a1fda09",
"ItemPropertySchema", 0, 0, 0, 0
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "c9eff54e-8164-4b21-a00e-4c4e9a1fda09"),
"itemType", "PodUserAccount"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "c9eff54e-8164-4b21-a00e-4c4e9a1fda09"),
"propertyName", "passwordHash"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "c9eff54e-8164-4b21-a00e-4c4e9a1fda09"),
"valueType", "Text"
);
-- salt: String
INSERT INTO items(id, type, dateCreated, dateModified, dateServerModified, deleted) VALUES(
"6defe71b-d43e-494c-abf4-423dc6c46acc",
"ItemPropertySchema", 0, 0, 0, 0
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "6defe71b-d43e-494c-abf4-423dc6c46acc"),
"itemType", "PodUserAccount"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "6defe71b-d43e-494c-abf4-423dc6c46acc"),
"propertyName", "salt"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "6defe71b-d43e-494c-abf4-423dc6c46acc"),
"valueType", "Text"
);
-- state: String
INSERT INTO items(id, type, dateCreated, dateModified, dateServerModified, deleted) VALUES(
"570dd24e-fe70-4566-8e45-e3de7a0e0d55",
"ItemPropertySchema", 0, 0, 0, 0
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "570dd24e-fe70-4566-8e45-e3de7a0e0d55"),
"itemType", "PodUserAccount"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "570dd24e-fe70-4566-8e45-e3de7a0e0d55"),
"propertyName", "state"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "570dd24e-fe70-4566-8e45-e3de7a0e0d55"),
"valueType", "Text"
);
-- code: String
INSERT INTO items(id, type, dateCreated, dateModified, dateServerModified, deleted) VALUES(
"be8d0096-b635-4de4-8b44-6f2a2acec0fa",
"ItemPropertySchema", 0, 0, 0, 0
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "be8d0096-b635-4de4-8b44-6f2a2acec0fa"),
"itemType", "PodUserAccount"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "be8d0096-b635-4de4-8b44-6f2a2acec0fa"),
"propertyName", "code"
);
INSERT INTO strings(item, name, value) VALUES(
(SELECT rowid FROM items WHERE id = "be8d0096-b635-4de4-8b44-6f2a2acec0fa"),
"valueType", "Text"
);
\ No newline at end of file
use reqwest::Method;
use secrecy::Secret;
use serde::de::{self, Visitor};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::ops::Deref;
use std::str::FromStr;
use std::{collections::HashMap, fmt::Debug, fmt::Display};
//
// Wrapper structs:
//
// TODO: create a macro for implementing truncated logging
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PodOwner(String);
impl Display for PodOwner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.0.as_str();
......@@ -22,7 +25,6 @@ impl Display for PodOwner {
}
}
use std::ops::Deref;
impl Deref for PodOwner {
type Target = String;
......@@ -45,6 +47,43 @@ impl FromStr for PodOwner {
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PodUserLogin(String);
/// In order to get full value use deref and reborrow: (&*login)
impl Display for PodUserLogin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.0.as_str();
if s.len() < 7 {
write!(f, "{s}")
} else {
write!(f, "{}..{}", &s[..5], &s[s.len() - 5..])
}
}
}
impl Deref for PodUserLogin {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<String> for PodUserLogin {
fn from(s: String) -> Self {
Self(s)
}
}
impl FromStr for PodUserLogin {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PodUserLogin(s.into()))
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PluginAuthData {
......@@ -365,11 +404,44 @@ pub struct PluginStatusRes {
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CreateUserReq {
pub struct PodCredentials {
pub owner_key: PodOwner,
pub database_key: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct UserAccountCredentials {
#[serde(deserialize_with = "deserialize_login")]
pub login: PodUserLogin,
pub password: Secret<String>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RegisterVerifyAccountReq {
#[serde(deserialize_with = "deserialize_login")]
pub login: PodUserLogin,
pub code: Secret<String>,
}
fn deserialize_login<'de, D>(deserializer: D) -> std::result::Result<PodUserLogin, D::Error>
where
D: Deserializer<'de>,
{
let mail: &str = Deserialize::deserialize(deserializer)?;
// Even though local part of the mail IS case sensitive, we convert to
// lowercase. Major mail service providers correctly assume it
// should be insensitive.
let login = PodUserLogin(mail.to_lowercase());
if !validator::validate_email(&login.0) {
return Err(serde::de::Error::custom("Invalid email format"));
}
Ok(login)
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PluginGetApiReq {
......
......@@ -2,6 +2,11 @@ use clap::Parser;
use lazy_static::lazy_static;
use std::net::IpAddr;
pub const DEFAULT_POD_DB_KEY: &str =
"54fec98071c745d0b812f97cb315f665e497301b39af499686ad8f0464663952";
pub const DEFAULT_POD_OWNER_KEY: &str =
"ad2e49e832214740af7ff3fa67d7b45bc0d8179346d443c9894b3ebe45978f54";
#[derive(Parser, Debug, Clone)]
#[command(
name = "Pod, the open-source backend for Memri project.",
......@@ -168,12 +173,22 @@ pub struct CliOptions {
pub email_smtp_password: Option<String>,
/// DB credentials used by the POD to store non volatile information
#[arg(long, env = "POD_OWNER_KEY_FOR_POD", requires = "db_key_for_pod")]
pub owner_key_for_pod: Option<String>,
#[arg(
long,
env = "POD_OWNER_KEY_FOR_POD",
requires = "db_key_for_pod",
default_value = DEFAULT_POD_DB_KEY
)]
pub owner_key_for_pod: String,
/// DB credentials used by the POD to store non volatile information
#[arg(long, env = "POD_DB_KEY_FOR_POD", requires = "owner_key_for_pod")]
pub db_key_for_pod: Option<String>,
#[arg(
long,
env = "POD_DB_KEY_FOR_POD",
requires = "owner_key_for_pod",
default_value = DEFAULT_POD_OWNER_KEY
)]
pub db_key_for_pod: String,
#[arg(long, env = "POD_SHARED_PLUGINS", action(clap::ArgAction::Append))]
pub shared_plugins: Vec<String>,
......@@ -240,8 +255,8 @@ pub mod tests {
email_smtp_port: 465,
email_smtp_user: None,
email_smtp_password: None,
owner_key_for_pod: None,
db_key_for_pod: None,
owner_key_for_pod: Default::default(),
db_key_for_pod: Default::default(),
shared_plugins: Vec::new(),
opened_connections: None,
}
......
......@@ -7,8 +7,6 @@ pub const FILES_DIR: &str = "./data/files";
/// (in future, the files should also be s3-uploaded).
pub const FILES_FINAL_SUBDIR: &str = "final";
pub const PLUGIN_EMAIL_SUBJECT_PREFIX: &str = "Memri plugin message: ";
pub const PLUGIN_EMAIL_FOOTER: &str =
"This is an automated message from a Memri plugin, do not reply.
pub const PLUGIN_EMAIL_FOOTER: &str = "This is an automated message from Memri, do not reply.
";
use crate::{
any_error,
async_db_connection::AsyncConnection,
bad_request, command_line_interface, constants, database_migrate_refinery,
command_line_interface, constants, database_migrate_refinery,
error::{ErrorContext, Result},
internal_error,
plugin_auth_crypto::{DatabaseKey, SHA256Output},
......@@ -52,7 +52,8 @@ pub async fn initialize_db(
) -> Result<()> {
let mut db = init_db.write().await;
if owner_database_path(owner).exists() {
return Err(bad_request! { "Account for {owner} already exist" });
// Database is already set up
return Ok(());
} else if db.get(owner).is_some() {
return Err(
internal_error! { "DB file does not exist but connection pool does for {owner}" },
......
......@@ -102,3 +102,28 @@ pub struct Oauth2Flow {
#[serde(flatten)]
pub base: ItemBase,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub enum RegisterState {
VerifyEmailSent,
RegistrationComplete,
EnforcePasswordReset,
}
pub const POD_ACCOUNT: &str = "PodUserAccount";
pub const LOGIN_HASH: &str = "loginHash";
pub const PASSWORD_HASH: &str = "passwordHash";
pub const SALT: &str = "salt";
pub const STATE: &str = "state";
pub const CODE: &str = "code";
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PodUserAccount {
pub login_hash: String,
/// In PHC format
pub password_hash: String,
pub salt: String,
pub state: RegisterState,
pub code: String,
}
use crate::{
api_model::SendEmail,
command_line_interface::CliOptions,
constants::{PLUGIN_EMAIL_FOOTER, PLUGIN_EMAIL_SUBJECT_PREFIX},
api_model::SendEmail, command_line_interface::CliOptions, constants::PLUGIN_EMAIL_FOOTER,
error::Result,
};
use lettre::{transport::smtp::authentication::Credentials, Message, SmtpTransport, Transport};
use tracing::{info, trace};
use lettre::{
transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncTransport, Message,
Tokio1Executor,
};
use tracing::{debug, info};
pub fn send_email(email: SendEmail, cli: &CliOptions) -> Result<()> {
trace!("Starting to send email: {:?}", email);
pub async fn send_email(email: SendEmail, cli: &CliOptions) -> Result<()> {
debug!("Starting to send email: {:?}", email);
match (
&cli.email_smtp_relay,
&cli.email_smtp_user,
......@@ -16,17 +17,18 @@ pub fn send_email(email: SendEmail, cli: &CliOptions) -> Result<()> {
) {
(Some(relay), Some(user), Some(password)) => {
let email = Message::builder()
.from(format!("Memri plugin <{user}>").parse()?)
.from(format!("Memri <{user}>").parse()?)
.to(email.to.parse()?)
.subject(format!("{}{}", PLUGIN_EMAIL_SUBJECT_PREFIX, email.subject))
.body(format!("{}{}", PLUGIN_EMAIL_FOOTER, email.body))?;
.subject(email.subject.to_string())
.body(format!("{}\n{}", email.body, PLUGIN_EMAIL_FOOTER))?;
let credentials: Credentials = Credentials::new(user.to_string(), password.to_string());
let server = SmtpTransport::relay(relay)?
let server = AsyncSmtpTransport::<Tokio1Executor>::relay(relay)?
.port(cli.email_smtp_port)
.credentials(credentials)
.timeout(Some(core::time::Duration::from_millis(5000)))
.build();
server.send(&email)?;
server.send(email).await?;
debug!("Mail sent");
Ok(())
}
_ => {
......
......@@ -19,6 +19,7 @@ pub mod plugin_auth_crypto;
pub mod plugin_run;
mod plugin_trigger;
pub mod schema;
pub mod shared_plugins;
pub mod shared_state;
pub mod test_helpers;
pub mod triggers;
pub mod user_account;
......@@ -512,12 +512,7 @@ fn kubernetes_set_resource_limits(cli_options: &CliOptions, plugin: &PluginRunIt
/// Returns true if pod_owner account is used to store shared data
fn is_pod_share_account(pod_owner: &str, cli: &CliOptions) -> bool {
pod_owner.to_ascii_lowercase()
== cli
.owner_key_for_pod
.as_ref()
.unwrap_or(&"".to_string())
.to_ascii_lowercase()
pod_owner.to_ascii_lowercase() == cli.owner_key_for_pod.to_ascii_lowercase()
}
pub fn get_logs(tx: &AsyncTx, plugin_run_id: &str, cli_options: &CliOptions) -> Result<Value> {
......
......@@ -2,47 +2,62 @@ use crate::{
api_model::{AuthKey, ClientAuth, CreateItem, Search},
async_db_connection::{AsyncConnection, AsyncTx},
command_line_interface::PARSED,
database_api::{self, dangerous_permament_remove_item},
database_api,
database_pool::{get_db_connection, initialize_db, InitDb},
db_model::ItemBase,
error::Result,
internal_api::{self, search},
error::{ErrorContext, Result},
internal_api,
plugin_auth_crypto::auth_to_database_key,
};
use std::ops::Deref;
use tracing::{debug, info, warn};
pub async fn initialize(init_db: &InitDb) -> Result<()> {
initialize_database(init_db)
.await
.context_str("While initializing shared database connection")?;
initialize_plugins(init_db)
.await
.context_str("While initializing shared plugins")
}
pub async fn initialize_database(init_db: &InitDb) -> Result<()> {
let (owner_key, database_key) = (
&PARSED.owner_key_for_pod,
auth_to_database_key(AuthKey::ClientAuth(ClientAuth {
database_key: PARSED.db_key_for_pod.clone(),
}))?,
);
initialize_db(owner_key, init_db, &database_key).await
}
pub async fn db_connection(init_db: &InitDb) -> Result<AsyncConnection> {
let (owner, database_key) = (
&PARSED.owner_key_for_pod,
auth_to_database_key(AuthKey::ClientAuth(ClientAuth {
database_key: PARSED.db_key_for_pod.clone(),
}))?,
);
get_db_connection(owner, init_db, &database_key).await
}
// TODO: there is no proper plugin state management
// if pod restarts - plugins are unable to reach it, plugin auth is broken
// plugins listens for that, and shuts down
// garbage in the db stays
// if plugin dies, pod does nothing with it
pub async fn db_connection(init_db: &InitDb) -> Result<Option<AsyncConnection>> {
if let (Some(owner), Some(database_key)) = (
PARSED.owner_key_for_pod.clone(),
PARSED.db_key_for_pod.clone(),
) {
let database_key = auth_to_database_key(AuthKey::ClientAuth(ClientAuth { database_key }))?;
let conn = get_db_connection(&owner, init_db, &database_key).await?;
Ok(Some(conn))
} else {
Ok(None)
}
}
pub async fn initialize(init_db: &InitDb) -> Result<()> {
let (Some(owner_key), Some(database_key)) = (PARSED.owner_key_for_pod.clone(), PARSED.db_key_for_pod.clone()) else {
info!("No POD credentials provided, skipping startup of shared plugins");
return Ok(());
};
let database_key = auth_to_database_key(AuthKey::ClientAuth(ClientAuth { database_key }))?;
let _ = initialize_db(&owner_key, init_db, &database_key).await;
pub async fn initialize_plugins(init_db: &InitDb) -> Result<()> {
let (owner_key, database_key) = (
&PARSED.owner_key_for_pod,
auth_to_database_key(AuthKey::ClientAuth(ClientAuth {
database_key: PARSED.db_key_for_pod.clone(),
}))?,
);
let mut conn = get_db_connection(&owner_key, init_db, &database_key).await?;
let mut conn = db_connection(init_db).await?;
// Remove PluginRun from the DB before starting new plugins
// Currently plugins have listeners that will detect lack of POD connection
......@@ -59,7 +74,7 @@ pub async fn initialize(init_db: &InitDb) -> Result<()> {
match internal_api::create_item(
payload,
&mut conn,
&owner_key,
owner_key,
&database_key,
PARSED.deref(),
)
......@@ -85,7 +100,7 @@ async fn clean_db_from_plugins(conn: &mut AsyncConnection) -> Result<()> {
..Default::default()
};
let plugins = search(&tx, &schema, query)?
let plugins = internal_api::search(&tx, &schema, query)?
.into_iter()
.map(|element| {
serde_json::from_value::<ItemBase>(element).unwrap_or_else(|err| {
......@@ -99,7 +114,7 @@ async fn clean_db_from_plugins(conn: &mut AsyncConnection) -> Result<()> {
for plugin in plugins {
debug!("Removing {}", plugin.id);
if let Err(e) = dangerous_permament_remove_item(&tx, plugin.rowid) {
if let Err(e) = database_api::dangerous_permament_remove_item(&tx, plugin.rowid) {
warn!(
"Failed to remove PluginRun with id {}, reason {e:?}",
plugin.id
......
......@@ -45,8 +45,8 @@ pub fn default_cli() -> CliOptions {
email_smtp_port: 465,
email_smtp_user: None,
email_smtp_password: None,
owner_key_for_pod: None,
db_key_for_pod: None,
owner_key_for_pod: Default::default(),
db_key_for_pod: Default::default(),
shared_plugins: Vec::new(),
opened_connections: None,
}
......
use std::collections::HashMap;
use argon2::password_hash::{Salt, SaltString};
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use rand::Rng;
use secrecy::{ExposeSecret, Secret};
use serde_json::json;
use sha2::{Digest, Sha256};
use tracing::{debug, info, instrument};
use crate::api_model::{
AuthKey, ClientAuth, CreateItem, PodCredentials, PodOwner, RegisterVerifyAccountReq, Search,
SendEmail, UserAccountCredentials,
};
use crate::async_db_connection::{AsyncConnection, AsyncTx};
use crate::command_line_interface::CliOptions;
use crate::database_pool::{initialize_db, InitDb};
use crate::db_model::{
ItemWithBase, PodUserAccount, RegisterState, CODE, LOGIN_HASH, PASSWORD_HASH, POD_ACCOUNT,
SALT, STATE,
};
use crate::email::send_email;
use crate::error::{ErrorContext, Result};
use crate::plugin_auth_crypto::auth_to_database_key;
use crate::{
bad_request, database_api, database_utils, internal_api, internal_error, shared_state,
};
#[instrument(fields(login=%body.login), skip_all)]
pub async fn register(
cli: &CliOptions,
init_db: &InitDb,
body: &UserAccountCredentials,
) -> Result<RegisterState> {
let mut conn = shared_state::db_connection(init_db).await?;
if let Some(acc) = get_account_from_db(&mut conn, &body.login).await? {
Err(bad_request!(
"Account already exists, status {:?}",
acc.item.state
))
} else {
let mut rnd = rand::thread_rng();
let code: Vec<i32> = (0..4).map(|_| rnd.gen_range(0..10)).collect();
let email = SendEmail {
to: (*body.login).to_string(),
subject: "Create new account".to_string(),
body: format!(
"Hey, use this token to continue with registration {} {} {} {}",
code[0], code[1], code[2], code[3]
),
};
send_email(email, cli).await?;
let password_hash = hash_password(body.password.clone()).await?;
let login_hash = hex::encode(Sha256::new_with_prefix(body.login.as_bytes()).finalize());
let code_str = format!("{}{}{}{}", code[0], code[1], code[2], code[3]);
let salt_for_db_keys = SaltString::generate(&mut rand::thread_rng());
let salt_string = salt_for_db_keys.to_string();
create_account_in_db(
&mut conn,
password_hash,
&salt_string,
&login_hash,
&code_str,
cli,
)
.await
}
}
#[instrument(fields(login=%body.login), skip_all)]
pub async fn verify(init_db: &InitDb, body: &RegisterVerifyAccountReq) -> Result<()> {
let mut conn = shared_state::db_connection(init_db).await?;
if let Some(mut acc) = get_account_from_db(&mut conn, &body.login).await? {
// Account already exists
if acc.item.state == RegisterState::VerifyEmailSent {
debug!("Verifying the account");
if &acc.item.code == body.code.expose_secret() {
debug!("Registration completed");
acc.item.state = RegisterState::RegistrationComplete;
update_account_in_db(&mut conn, &acc).await
} else {
Err(bad_request!("Invalid token provided"))
}
} else {
Err(bad_request!(
"Cannot verify account, invalid state {:?}",
acc.item.state
))
}
} else {
Err(bad_request!("Account does not exist"))
}
}
/// Get POD keys from user credentials
#[instrument(fields(login=%body.login), skip_all)]
pub async fn get_pod_keys(
init_db: &InitDb,
body: &UserAccountCredentials,
) -> Result<PodCredentials> {
debug!("Deriving keys");
let mut conn = shared_state::db_connection(init_db).await?;
let Some(acc) = get_account_from_db(&mut conn, &body.login).await? else {
return Err(bad_request!("Account does not exist"));
};
if acc.item.state != RegisterState::RegistrationComplete {
return Err(bad_request!("Account is not verified"));
}
validate_password(body.password.clone(), acc.item.password_hash.clone()).await?;
let salt = SaltString::from_b64(&acc.item.salt).expect("Invalid salt format");
let owner_key = derive_pod_key(Secret::new(body.login.to_string()), salt.clone()).await?;
let database_key = derive_pod_key(body.password.clone(), salt).await?;
Ok(PodCredentials {
owner_key: PodOwner::from(owner_key.expose_secret().clone()),
database_key: database_key.expose_secret().clone(),
})
}
#[instrument(fields(owner=%body.owner_key), skip_all)]
pub async fn open_pod(init_db: &InitDb, body: PodCredentials) -> Result<PodOwner> {
info!("Opening POD DB");
database_utils::check_owner(&body.owner_key)?;
let database_key = auth_to_database_key(AuthKey::ClientAuth(ClientAuth {
database_key: body.database_key,
}))?;
initialize_db(&body.owner_key, init_db, &database_key).await?;
debug!("POD DB ready");
Ok(body.owner_key)
}
async fn validate_password(password: Secret<String>, expected_hash: String) -> Result<()> {
tokio::task::spawn_blocking(move || {
Argon2::default()
.verify_password(
password.expose_secret().as_bytes(),
&PasswordHash::new(&expected_hash).expect("Invalid PHC string format"),
)
.map_err(|e| bad_request!("Invalid password {e}"))?;
Ok(())
})
.await?
}
/// Generate POD key from the input and provided salt. Returns hexstring.
async fn derive_pod_key(input: Secret<String>, salt: SaltString) -> Result<Secret<String>> {
tokio::task::spawn_blocking(move || {
let mut raw_key = [0u8; 32];
let mut raw_salt = [0u8; Salt::RECOMMENDED_LENGTH];
salt.decode_b64(&mut raw_salt)
.map_err(|e| internal_error!("While decoding salt {e}"))?;
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
Argon2::default()
.hash_password_into(input.expose_secret().as_bytes(), &raw_salt, &mut raw_key)
.map_err(|e| internal_error!("While deriving the pod key {}", e))?;
Ok(Secret::new(hex::encode(raw_key)))
})
.await?
}
/// Generate argon2 hash with unique salt. Returns PHC string.
async fn hash_password(password: Secret<String>) -> Result<String> {
tokio::task::spawn_blocking(move || {
let salt = SaltString::generate(&mut rand::thread_rng());
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
let phc_hash = Argon2::default()
.hash_password(password.expose_secret().as_bytes(), salt.as_salt())
.map_err(|e| internal_error!("While hashing the password {}", e))?
.to_string();
Ok(phc_hash)
})
.await?
}
async fn create_account_in_db(
conn: &mut AsyncConnection,
password_hash: String,
salt: &str,
login_hash: &str,
code: &str,
cli: &CliOptions,
) -> Result<RegisterState> {
conn.in_write_transaction(|tx: AsyncTx| async move {
let mut schema = database_api::get_schema(&tx)?;
let state = RegisterState::VerifyEmailSent;
let item = CreateItem {
_type: POD_ACCOUNT.to_string(),
fields: json!({
LOGIN_HASH: login_hash,
PASSWORD_HASH: password_hash,
SALT: salt,
CODE: code,
STATE: state,
})
.as_object()
.expect("Invalid json object for PodAccount")
.clone()
.into_iter()
.collect(),
..Default::default()
};
let id = internal_api::create_item_tx(&tx, &mut schema, item, "", cli).await?;
debug!("PodAccount created {id}");
Ok(state)
})
.await
}
async fn get_account_from_db(
conn: &mut AsyncConnection,
login: &str,
) -> Result<Option<ItemWithBase<PodUserAccount>>> {
let login_hash = hex::encode(Sha256::new_with_prefix(login.as_bytes()).finalize());
let mut res = conn
.in_read_transaction(|tx: AsyncTx| async move {
let schema = database_api::get_schema(&tx)?;
let query = Search {
_type: Some(POD_ACCOUNT.to_string()),
deleted: Some(false),
limit: u64::MAX,
other_properties: HashMap::from([(
LOGIN_HASH.to_string(),
serde_json::to_value(login_hash).expect("Cannot serialize email_hash to Value"),
)]),
..Default::default()
};
internal_api::search(&tx, &schema, query)
})
.await?;
debug_assert!(
res.len() <= 1,
"There is more than one PodAccount entry in the database for {:?}",
login
);
if let Some(value) = res.pop() {
let pod_account: ItemWithBase<PodUserAccount> = serde_json::from_value(value)
.context(|| format!("Cannot deserialize PodAccount for {:?}", login))?;
debug!("Found PodAccount item {:#?}", pod_account);
Ok(Some(pod_account))
} else {
Ok(None)
}
}
async fn update_account_in_db(
conn: &mut AsyncConnection,
acc: &ItemWithBase<PodUserAccount>,
) -> Result<()> {
conn.in_write_transaction(|tx: AsyncTx| async move {
let schema = database_api::get_schema(&tx)?;
internal_api::update_item_tx(
&tx,
&schema,
&acc.base.id,
serde_json::to_value(acc.item.clone())
.expect("Unable to convert PodAccount to Value")
.as_object()
.expect("Invalid json object for PodAccount")
.clone()
.into_iter()
.collect(),
)?;
debug!("PodAccount {} updated", acc.item.login_hash);
Ok(())
})
.await
}
......@@ -76,8 +76,8 @@ required-features = ["include_slow_tests"]
[[test]]
name = "test_create_account"
path = "tests/test_create_account.rs"
name = "test_pod_keys"
path = "tests/test_pod_keys.rs"
required-features = ["include_slow_tests"]
......
use crate::actix_endpoints::{
bulk, create_account, create_edge, create_item, delete_edge_by_source_target, delete_item,
delete_user, get_edges, get_file, get_item, get_logs, graphql, not_found, oauth1_access_token,
oauth1_request_token, oauth2_access_token, oauth2_auth_url, oauth2_authorize, plugin_api,
plugin_api_call, plugin_attach, plugins_status, search, trace_filter, trigger_status,
update_item, upload_file, upload_file_b, version,
account, account_derive_pod_keys, account_register, account_verify, bulk, create_edge,
create_item, delete_edge_by_source_target, delete_item, delete_pod, get_edges, get_file,
get_item, get_logs, graphql, not_found, oauth1_access_token, oauth1_request_token,
oauth2_access_token, oauth2_auth_url, oauth2_authorize, open_pod, plugin_api, plugin_api_call,
plugin_attach, plugins_status, search, send_email, trace_filter, trigger_status, update_item,
upload_file, upload_file_b, version,
};
use actix_cors::Cors;
use actix_web::{
......@@ -15,7 +16,7 @@ use libpod::{
command_line_interface::CliOptions,
database_pool::ConnectionPool,
error::{ErrorContext, Result},
internal_error, shared_plugins,
internal_error, shared_state,
};
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
......@@ -43,7 +44,7 @@ pub async fn run_server<S: 'static>(
HashMap::<String, ConnectionPool>::new(),
));
let shared_plugin_db = init_db.clone();
let shared_state = init_db.clone();
let cli_options = web::Data::new(cli_options);
let trace_handle = web::Data::new(trace_handle);
......@@ -65,6 +66,7 @@ pub async fn run_server<S: 'static>(
.route("/trace_filter", web::post().to(trace_filter::<S>))
.service(
web::scope("/v4")
.service(send_email)
.service(create_item)
.service(get_item)
.service(oauth1_request_token)
......@@ -75,8 +77,9 @@ pub async fn run_server<S: 'static>(
.service(update_item)
.service(bulk)
.service(delete_item)
.service(delete_user)
.service(create_account)
.service(delete_pod)
.service(open_pod)
.service(account)
.service(create_edge)
.service(delete_edge_by_source_target)
.service(get_edges)
......@@ -109,7 +112,10 @@ pub async fn run_server<S: 'static>(
.service(plugin_attach),
)
.service(get_logs)
.service(trigger_status),
.service(trigger_status)
.service(account_register)
.service(account_verify)
.service(account_derive_pod_keys),
)
});
......@@ -165,10 +171,10 @@ pub async fn run_server<S: 'static>(
Ok(())
});
// Note initializing shared plugins must happen after webserver is up and ready,
// Note initializing shared state and plugins in particular must happen after webserver is up and ready,
// otherwise starting plugin might not be able to reach the pod - RC for the webserver
if let Err(e) = shared_plugins::initialize(shared_plugin_db.deref()).await {
warn!("Failed to initialize shared plugins: {e}");
if let Err(e) = shared_state::initialize(shared_state.deref()).await {
warn!("Failed to initialize shared state: {e}");
}
server_handle.await?
......
......@@ -169,26 +169,81 @@ pub async fn delete_item(
}
#[instrument(fields(uid=trace_uid(), %owner), skip_all)]
#[post("{owner}/delete_user")]
pub async fn delete_user(
#[post("{owner}/delete_pod")]
pub async fn delete_pod(
owner: web::Path<PodOwner>,
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
let body = extract_json(&body)?;
let result = pod_handlers::delete_user(owner.to_owned(), &init_db.to_owned(), body).await;
let result = pod_handlers::delete_pod(owner.to_owned(), &init_db.to_owned(), body).await;
let result = result.map(|()| web::Json(serde_json::json!({})));
respond_with_result(result)
}
// TODO: deprecated
#[instrument(fields(uid=trace_uid()), skip_all)]
#[post("/account")]
pub async fn create_account(
pub async fn account(
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
do_open_pod(init_db, body).await
}
#[post("/account/pod/open")]
pub async fn open_pod(
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
do_open_pod(init_db, body).await
}
// TODO: current implementation allows everyone to reach this endpoint and
// do as many pods as you like. Changing this will break the pymemri and plugins tests.
async fn do_open_pod(
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
let body = extract_json(&body)?;
let result = pod_handlers::open_pod(&init_db.into_inner(), body).await;
let result = result.map(|result| web::Json(serde_json::json!(result)));
respond_with_result(result)
}
#[instrument(fields(uid=trace_uid()), skip_all)]
#[post("/account/register")]
pub async fn account_register(
cli: web::Data<CliOptions>,
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
let body = extract_json(&body)?;
let result = pod_handlers::account_register(&cli, &init_db.into_inner(), body).await;
let result = result.map(|result| web::Json(serde_json::json!(result)));
respond_with_result(result)
}
#[instrument(fields(uid=trace_uid()), skip_all)]
#[post("/account/verify")]
pub async fn account_verify(
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
let body = extract_json(&body)?;
let result = pod_handlers::account_verify(&init_db.into_inner(), body).await;
let result = result.map(|result| web::Json(serde_json::json!(result)));
respond_with_result(result)
}
#[instrument(fields(uid=trace_uid()), skip_all)]
#[post("/account/derive_pod_keys")]
pub async fn account_derive_pod_keys(
init_db: web::Data<InitDb>,
body: web::Bytes,
) -> actix_web::Result<impl Responder> {
let body = extract_json(&body)?;
let result = pod_handlers::create_account(&init_db.into_inner(), body).await;
let result = pod_handlers::account_derive_pod_keys(&init_db.into_inner(), body).await;
let result = result.map(|result| web::Json(serde_json::json!(result)));
respond_with_result(result)
}
......
......@@ -5,25 +5,25 @@ use http::StatusCode;
use libpod::{
any_error,
api_model::{
AuthKey, Bulk, BulkResponse, ClientAuth, CreateEdge, CreateItem, CreateUserReq,
DeleteEdgeBySourceTarget, GetEdges, GetFile, Oauth2AccessTokenRequest,
Oauth2AccessTokenResponse, Oauth2AuthUrlRequest, Oauth2AuthUrlResponse,
Oauth2AuthorizeTokenRequest, OauthAccessTokenPayload, OauthRequestTokenPayload,
PayloadWrapper, PluginAttachReq, PluginCallApiReq, PluginCallApiRes, PluginGetApiReq,
PluginGetApiRes, PluginStatusReq, PluginStatusRes, PodOwner, Search, SendEmail,
TraceRequest, UpdateItem,
AuthKey, Bulk, BulkResponse, CreateEdge, CreateItem, DeleteEdgeBySourceTarget, GetEdges,
GetFile, Oauth2AccessTokenRequest, Oauth2AccessTokenResponse, Oauth2AuthUrlRequest,
Oauth2AuthUrlResponse, Oauth2AuthorizeTokenRequest, OauthAccessTokenPayload,
OauthRequestTokenPayload, PayloadWrapper, PluginAttachReq, PluginCallApiReq,
PluginCallApiRes, PluginGetApiReq, PluginGetApiRes, PluginStatusReq, PluginStatusRes,
PodCredentials, PodOwner, RegisterVerifyAccountReq, Search, SendEmail, TraceRequest,
UpdateItem, UserAccountCredentials,
},
async_db_connection::AsyncTx,
bad_request,
command_line_interface::CliOptions,
database_api::{self},
database_pool::{initialize_db, InitDb},
database_pool::InitDb,
database_utils, email,
error::Result,
error::{ErrorContext, ErrorType},
file_api, internal_api, internal_error, oauth1_api, oauth2_api,
plugin_auth_crypto::{auth_to_database_key, DatabaseKey},
plugin_run, shared_plugins, triggers,
plugin_run, shared_state, triggers, user_account,
};
use serde_json::Value;
use sha2::{Digest, Sha256};
......@@ -31,7 +31,7 @@ use std::{
collections::HashMap,
sync::{atomic::AtomicU16, Arc},
};
use tokio::task;
use tracing::{debug, info, warn};
use tracing_subscriber::{reload::Handle, EnvFilter};
......@@ -209,7 +209,7 @@ pub async fn delete_item(
.await
}
#[inline(always)]
pub async fn delete_user(
pub async fn delete_pod(
owner: PodOwner,
init_db: &InitDb,
body: PayloadWrapper<String>,
......@@ -220,21 +220,42 @@ pub async fn delete_user(
database_utils::check_owner_and_delete_db(&owner, init_db, &database_key).await?;
Result::Ok(())
}
#[inline(always)]
pub async fn create_account(init_db: &InitDb, body: CreateUserReq) -> Result<PodOwner> {
info!("Creating account for {}", body.owner_key);
database_utils::check_owner(&body.owner_key)?;
pub async fn open_pod(init_db: &InitDb, body: PodCredentials) -> Result<PodOwner> {
user_account::open_pod(init_db, body).await
}
let database_key = auth_to_database_key(AuthKey::ClientAuth(ClientAuth {
database_key: body.database_key,
}))?;
#[inline(always)]
pub async fn account_register(
cli: &CliOptions,
init_db: &InitDb,
body: UserAccountCredentials,
) -> Result<()> {
info!("Register account for {:?}", body.login);
initialize_db(&body.owner_key, init_db, &database_key).await?;
user_account::register(cli, init_db, &body).await?;
debug!("Account created");
Ok(body.owner_key)
Ok(())
}
#[inline(always)]
pub async fn account_verify(init_db: &InitDb, body: RegisterVerifyAccountReq) -> Result<()> {
info!("Verify account for {:?}", body.login);
user_account::verify(init_db, &body).await
}
#[inline(always)]
pub async fn account_derive_pod_keys(
init_db: &InitDb,
body: UserAccountCredentials,
) -> Result<PodCredentials> {
user_account::get_pod_keys(init_db, &body).await
}
#[inline(always)]
pub async fn create_edge(
owner: PodOwner,
......@@ -410,12 +431,14 @@ pub async fn send_email(
) -> Result<()> {
let auth = body.auth;
let payload = body.payload;
// Get conn to validate creds
let database_key = auth_to_database_key(auth)?;
let _conn =
database_utils::check_owner_and_initialize_db(&owner, init_db, &database_key).await?;
let cli = cli.clone();
task::spawn_blocking(move || email::send_email(payload, &cli)).await?
email::send_email(payload, &cli).await
}
#[inline(always)]
......@@ -459,11 +482,9 @@ pub async fn plugins_status(
let plugins = plugin_run::get_plugins_status(&mut conn, cli, &body.payload).await?;
let shared_plugins = if let Some(mut conn) = shared_plugins::db_connection(init_db).await? {
plugin_run::get_plugins_status(&mut conn, cli, &body.payload).await?
} else {
HashMap::new()
};
let mut conn = shared_state::db_connection(init_db).await?;
let shared_plugins = plugin_run::get_plugins_status(&mut conn, cli, &body.payload).await?;
Ok(PluginStatusRes {
plugins,
......@@ -492,9 +513,8 @@ pub async fn plugin_api(
if plugin_not_found {
// Plugin not found, maybe it's shared plugin?
if let Some(mut conn) = shared_plugins::db_connection(init_db).await? {
api = plugin_run::get_plugin_api(&mut conn, cli, &body.payload).await;
}
let mut conn = shared_state::db_connection(init_db).await?;
api = plugin_run::get_plugin_api(&mut conn, cli, &body.payload).await;
}
api
......@@ -521,9 +541,8 @@ pub async fn plugin_api_call(
if plugin_not_found {
// Plugin not found, maybe it's shared plugin?
if let Some(mut conn) = shared_plugins::db_connection(init_db).await? {
api_response = plugin_run::call_plugin_api(&mut conn, cli, &body.payload).await
}
let mut conn = shared_state::db_connection(init_db).await?;
api_response = plugin_run::call_plugin_api(&mut conn, cli, &body.payload).await;
}
api_response
......
......@@ -31,12 +31,10 @@ impl PodClient {
}
/// Convenient function to do POST request with given payload to the POD
pub async fn post_to<T>(&self, payload: T, endpoint: &str) -> Response
pub async fn post_to_with_owner<T>(&self, payload: T, endpoint: &str) -> Response
where
T: Serialize + Debug,
{
debug!("POST: {endpoint}, body {payload:#?}");
let body = PayloadWrapper {
auth: AuthKey::ClientAuth(libpod::api_model::ClientAuth {
database_key: self.database_key.clone(),
......@@ -44,12 +42,19 @@ impl PodClient {
payload,
};
self.post_to(body, &format!("{}/{endpoint}", self.owner_key))
.await
}
pub async fn post_to<T>(&self, payload: T, endpoint: &str) -> Response
where
T: Serialize + Debug,
{
debug!("POST: {endpoint}, body {payload:#?}");
self.client
.post(&format!(
"{}/v4/{}/{endpoint}",
self.pod_url, self.owner_key
))
.json(&body)
.post(&format!("{}/v4/{endpoint}", self.pod_url))
.json(&payload)
.send()
.await
.unwrap()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment