From 758ac8f850dd2f6200e875af1c4b7f7b39497d9b Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 16 Dec 2024 20:08:50 -0500 Subject: [PATCH] Add support for sync to AWS (#3723) This is closely modeled on support for sync to GCP (#3223), but with different authentication options to mirror typical usage of AWS. --- Cargo.lock | 424 +++++++++++++++++++++----------- doc/man/task-sync.5.in | 77 ++++++ src/Context.cpp | 6 + src/commands/CmdShow.cpp | 8 +- src/commands/CmdSync.cpp | 52 +++- src/taskchampion-cpp/Cargo.toml | 2 +- src/taskchampion-cpp/src/lib.rs | 87 +++++++ 7 files changed, 506 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2943a2c54..56f3ab7a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,8 +417,8 @@ dependencies = [ "http-body 0.4.6", "http-body 1.0.1", "httparse", - "hyper", - "hyper-rustls", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", "pin-utils", @@ -505,7 +505,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -542,12 +542,6 @@ 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 = "bitflags" version = "2.6.0" @@ -606,6 +600,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -618,7 +618,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1054,9 +1054,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "google-cloud-auth" -version = "0.13.2" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf7cb7864f08a92e77c26bb230d021ea57691788fb5dd51793f96965d19e7f9" +checksum = "e57a13fbacc5e9c41ded3ad8d0373175a6b7a6ad430d99e89d314ac121b7ab06" dependencies = [ "async-trait", "base64 0.21.7", @@ -1076,9 +1076,9 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" +checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" dependencies = [ "reqwest", "thiserror 1.0.64", @@ -1087,10 +1087,11 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "0.15.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" +checksum = "e81dff54dbfa83705c896179ecaa4f384bfbfac90f3b637f38541443275b8a3f" dependencies = [ + "anyhow", "async-stream", "async-trait", "base64 0.21.7", @@ -1105,6 +1106,7 @@ dependencies = [ "pkcs8 0.10.2", "regex", "reqwest", + "reqwest-middleware", "ring", "serde", "serde_json", @@ -1312,6 +1314,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1320,12 +1341,49 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.30", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.1", + "hyper-util", + "rustls 0.23.19", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.1", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -1738,7 +1796,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1810,6 +1868,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -1819,6 +1886,58 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.19", + "socket2", + "thiserror 2.0.6", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.19", + "rustls-pki-types", + "slab", + "thiserror 2.0.6", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -1828,6 +1947,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1843,7 +1983,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -1883,20 +2023,21 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper", - "hyper-rustls", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.1", + "hyper-rustls 0.27.3", + "hyper-util", "ipnet", "js-sys", "log", @@ -1905,15 +2046,16 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "quinn", + "rustls 0.23.19", + "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.1", "tokio-util", "tower-service", "url", @@ -1921,8 +2063,23 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.4", - "winreg", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "reqwest-middleware" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" +dependencies = [ + "anyhow", + "async-trait", + "http 1.2.0", + "reqwest", + "serde", + "thiserror 1.0.64", + "tower-service", ] [[package]] @@ -1957,7 +2114,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.6.0", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1971,6 +2128,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2055,6 +2218,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -2140,7 +2306,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2367,9 +2533,12 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -2382,32 +2551,11 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "taskchampion" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e1f314f98be15c04291e62ff356f1f4165977c7c5baafe90258761876b84f1" +checksum = "c4e41712ec2dd9cb5e7f4f20daf13a8e0937a927fb886153f2523a6686c35983" dependencies = [ "anyhow", "aws-config", @@ -2530,6 +2678,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -2569,6 +2732,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.19", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -2672,7 +2845,7 @@ dependencies = [ "rustls-native-certs 0.7.3", "rustls-pki-types", "url", - "webpki-roots 0.26.6", + "webpki-roots", ] [[package]] @@ -2838,10 +3011,14 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.25.4" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "webpki-roots" @@ -2867,49 +3044,55 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.48.5", + "windows-result", + "windows-strings", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-targets 0.52.6", + "windows-result", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2918,46 +3101,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2970,64 +3135,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "write16" version = "1.0.0" @@ -3076,6 +3207,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 18f8a8189..a2dacf23b 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -139,6 +139,83 @@ Then configure Taskwarrior with: $ task config sync.gcp.credential_path .fi +.SS Amazon Web Services + +To synchronize your tasks to AWS, select a region near you and use the AWS +console to create a new S3 bucket. The default settings for the bucket are +adequate. + +You will also need an AWS IAM user with the following policy, where BUCKETNAME +is the name of the bucket. The same user can be configured for multiple +Taskwarrior clients. + +.nf + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "TaskChampion", + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:ListBucket", + "s3:DeleteObject" + ], + "Resource": [ + "arn:aws:s3:::BUCKETNAME", + "arn:aws:s3:::BUCKETNAME/*" + ] + } + ] + } +.fi + +To create such a user, create a new policy in the IAM console, select the JSON +option in the policy editor, and paste the policy. Click "Next" and give the +policy a name such as "TaskwarriorSync". Next, create a new user, with a name +of your choosing, select "Attach Policies Directly", and then choose the +newly-created policy. + +You will need access keys configured for the new user. Find the user in the +user list, open the "Security Credentials" tab, then click "Create access key" +and follow the steps. + +At this point, you can choose how to provide those credentials to Taskwarrior. +The simplest is to include them in the Taskwarrior configuration: + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.access_key_id + $ task config sync.aws.secret_access_key +.fi + +Alternatively, you can set up an AWS CLI profile, using a profile name of your +choosing such as "taskwarrior-creds": + +.nf + $ aws configure --profile taskwarrior-creds +.fi + +Enter the access key ID and secret access key. The default region and format +are not important. Then configure Taskwarrior with: + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.profile taskwarrior-creds +.fi + +To use AWS's default credential sources, such as environment variables, the +default profile, or an instance profile, set + +.nf + $ task config sync.aws.region + $ task config sync.aws.bucket + $ task config sync.aws.default_credentials true +.fi + .SS Local Synchronization In order to take advantage of synchronization's side effect of saving disk diff --git a/src/Context.cpp b/src/Context.cpp index c858f4373..532a2699b 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -318,6 +318,12 @@ std::string configurationDefaults = "#sync.server.client_id # Client ID for sync to a server\n" "#sync.server.url # URL of the sync server\n" "#sync.local.server_dir # Directory for local sync\n" + "#sync.aws.region # region for AWS sync\n" + "#sync.aws.bucket # bucket for AWS sync\n" + "#sync.aws.access_key_id # access_key_id for AWS sync\n" + "#sync.aws.secret_access_key # secret_access_key for AWS sync\n" + "#sync.aws.profile # profile name for AWS sync\n" + "#sync.aws.default_credentials # use default credentials for AWS sync\n" "#sync.gcp.credential_path # Path to JSON file containing credentials to " "authenticate GCP Sync\n" "#sync.gcp.bucket # Bucket for sync to GCP\n" diff --git a/src/commands/CmdShow.cpp b/src/commands/CmdShow.cpp index df92df6a1..a5f76e1f2 100644 --- a/src/commands/CmdShow.cpp +++ b/src/commands/CmdShow.cpp @@ -194,9 +194,15 @@ int CmdShow::execute(std::string& output) { " search.case.sensitive" " sugar" " summary.all.projects" - " sync.local.server_dir" + " sync.aws.access_key_id" + " sync.aws.bucket" + " sync.aws.default_credentials" + " sync.aws.profile" + " sync.aws.region" + " sync.aws.secret_access_key" " sync.gcp.credential_path" " sync.gcp.bucket" + " sync.local.server_dir" " sync.server.client_id" " sync.encryption_secret" " sync.server.url" diff --git a/src/commands/CmdSync.cpp b/src/commands/CmdSync.cpp index 361df7f4d..e4f07e8f6 100644 --- a/src/commands/CmdSync.cpp +++ b/src/commands/CmdSync.cpp @@ -38,6 +38,7 @@ #include #include +#include #include //////////////////////////////////////////////////////////////////////////////// @@ -65,12 +66,11 @@ int CmdSync::execute(std::string& output) { bool avoid_snapshots = false; bool verbose = Context::getContext().verbose("sync"); - // If no server is set up, quit. std::string origin = Context::getContext().config.get("sync.server.origin"); std::string url = Context::getContext().config.get("sync.server.url"); std::string server_dir = Context::getContext().config.get("sync.local.server_dir"); std::string client_id = Context::getContext().config.get("sync.server.client_id"); - std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); + std::string aws_bucket = Context::getContext().config.get("sync.aws.bucket"); std::string gcp_bucket = Context::getContext().config.get("sync.gcp.bucket"); std::string encryption_secret = Context::getContext().config.get("sync.encryption_secret"); @@ -85,7 +85,55 @@ int CmdSync::execute(std::string& output) { out << format("Syncing with {1}", server_dir) << '\n'; } replica->sync_to_local(server_dir, avoid_snapshots); + } else if (aws_bucket != "") { + std::string aws_region = Context::getContext().config.get("sync.aws.region"); + std::string aws_profile = Context::getContext().config.get("sync.aws.profile"); + std::string aws_access_key_id = Context::getContext().config.get("sync.aws.access_key_id"); + std::string aws_secret_access_key = + Context::getContext().config.get("sync.aws.secret_access_key"); + std::string aws_default_credentials = + Context::getContext().config.get("sync.aws.default_credentials"); + if (aws_region == "") { + throw std::string("sync.aws.region is required"); + } + if (encryption_secret == "") { + throw std::string("sync.encryption_secret is required"); + } + + bool using_profile = false; + bool using_creds = false; + bool using_default = false; + if (aws_profile != "") { + using_profile = true; + } + if (aws_access_key_id != "" || aws_secret_access_key != "") { + using_creds = true; + } + if (aws_default_credentials != "") { + using_default = true; + } + + if (using_profile + using_creds + using_default != 1) { + throw std::string("exactly one method of specifying AWS credentials is required"); + } + + if (verbose) { + out << format("Syncing with AWS bucket {1}", aws_bucket) << '\n'; + } + + if (using_profile) { + replica->sync_to_aws_with_profile(aws_region, aws_bucket, aws_profile, encryption_secret, + avoid_snapshots); + } else if (using_creds) { + replica->sync_to_aws_with_access_key(aws_region, aws_bucket, aws_access_key_id, + aws_secret_access_key, encryption_secret, + avoid_snapshots); + } else { + replica->sync_to_aws_with_default_creds(aws_region, aws_bucket, encryption_secret, + avoid_snapshots); + } } else if (gcp_bucket != "") { + std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path"); if (encryption_secret == "") { throw std::string("sync.encryption_secret is required"); } diff --git a/src/taskchampion-cpp/Cargo.toml b/src/taskchampion-cpp/Cargo.toml index b5e75a0d8..b806f986f 100644 --- a/src/taskchampion-cpp/Cargo.toml +++ b/src/taskchampion-cpp/Cargo.toml @@ -9,7 +9,7 @@ rust-version = "1.78.0" # MSRV crate-type = ["staticlib"] [dependencies] -taskchampion = "1.0.0" +taskchampion = "=1.0.2" cxx = "1.0.133" [features] diff --git a/src/taskchampion-cpp/src/lib.rs b/src/taskchampion-cpp/src/lib.rs index 829394f63..6fb8ace0a 100644 --- a/src/taskchampion-cpp/src/lib.rs +++ b/src/taskchampion-cpp/src/lib.rs @@ -164,6 +164,36 @@ mod ffi { avoid_snapshots: bool, ) -> Result<()>; + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::Profile`. + fn sync_to_aws_with_profile( + &mut self, + region: String, + bucket: String, + profile_name: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::AccessKey`. + fn sync_to_aws_with_access_key( + &mut self, + region: String, + bucket: String, + access_key_id: String, + secret_access_key: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + + /// Sync with a server created from `ServerConfig::Aws` using `AwsCredentials::Default`. + fn sync_to_aws_with_default_creds( + &mut self, + region: String, + bucket: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<()>; + /// Sync with a server created from `ServerConfig::Gcp`. /// /// An empty value for `credential_path` is converted to `Option::None`. @@ -580,6 +610,63 @@ impl Replica { Ok(self.0.sync(&mut server, avoid_snapshots)?) } + fn sync_to_aws_with_profile( + &mut self, + region: String, + bucket: String, + profile_name: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::Profile { profile_name }, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_aws_with_access_key( + &mut self, + region: String, + bucket: String, + access_key_id: String, + secret_access_key: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::AccessKey { + access_key_id, + secret_access_key, + }, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + + fn sync_to_aws_with_default_creds( + &mut self, + region: String, + bucket: String, + encryption_secret: &CxxString, + avoid_snapshots: bool, + ) -> Result<(), CppError> { + let mut server = tc::server::ServerConfig::Aws { + region, + bucket, + credentials: tc::server::AwsCredentials::Default, + encryption_secret: encryption_secret.as_bytes().to_vec(), + } + .into_server()?; + Ok(self.0.sync(&mut server, avoid_snapshots)?) + } + fn sync_to_gcp( &mut self, bucket: String,