WIP feat (kv2): Method implementations and test improvements
This commit is contained in:
parent
b5e086bd0a
commit
491ca2fd54
20 changed files with 1337 additions and 852 deletions
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n UPDATE kv2_secret_version\n SET deletion_time = $4\n WHERE engine_path = $1 AND secret_path = $2\n AND version_number = $3\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 4
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "047ebbce6fa0073cc810b189e8db3ff5e4eb347f1c1d9e5408220411a9e08b00"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT secret_data, created_time, deletion_time, version_number, secret_path\n FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL\n ORDER BY version_number DESC LIMIT 1",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "secret_data",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "created_time",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deletion_time",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Datetime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version_number",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "secret_path",
|
||||||
|
"ordinal": 4,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "0b6d152060335c7c8cc6e781eac810a3fc1c2ad752e3f856a66e75b73b2ab3c6"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n SELECT version_number AS latest_version FROM kv2_secret_version\n WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL\n ORDER BY version_number DESC LIMIT 1",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "latest_version",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "414c74a3c017bde424fe44bbc251fea384b0dbedd1541900d147e0814c1f33d8"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "SELECT engine_type FROM secret_engines WHERE mount_point = $1",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "engine_type",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 1
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "9265f0195bbacd15061927c2a6034e3725a25068fd3faa08cc1d02e7c926f1c2"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n INSERT INTO kv2_metadata (engine_path, secret_path, cas_required, created_time, max_versions, updated_time)\n VALUES ($1, $2, 0, $3, 100, $3)\n ON CONFLICT(engine_path, secret_path) DO NOTHING;\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 3
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "af57fe92ead35790b02f38f34e1614cd1accb2da61f1d9a07eeefb0fc31ec318"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\nWITH latest_version AS (\n SELECT MAX(version_number) AS max_version\n FROM kv2_secret_version\n WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path\n)\nINSERT INTO kv2_secret_version (engine_path, secret_path, secret_data, created_time, version_number)\nVALUES (\n $1, -- engine_path\n $2, -- secret_path\n $3, -- secret_data\n $4, -- created_time\n CASE -- Use provided version if given\n WHEN $5 IS NOT NULL THEN $5 -- version_number (optional)\n ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 0)\n END -- version_number logic\n)\nRETURNING version_number;\n",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "version_number",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 5
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "c6beeb7d8672039df5258ada802920aae8f16db215dda5ab447dbe832f4a6703"
|
||||||
|
}
|
||||||
1404
Cargo.lock
generated
1404
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
|
@ -7,23 +7,25 @@ edition = "2021"
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
|
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
|
||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
time = { version = "0.3.39", features = ["serde", "formatting"]}
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
tower = { version = "0.4.13", features = [] }
|
tower = { version = "0.5.2", features = [] }
|
||||||
axum = "0.7.5"
|
axum = "0.8.1"
|
||||||
serde = "1.0.201"
|
serde = "1.0.201"
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.117"
|
||||||
json-patch = "2.0.0"
|
json-patch = "4.0.0"
|
||||||
# serde_with = "3.8.1"
|
# serde_with = "3.8.1"
|
||||||
|
dotenvy = "0.15.7"
|
||||||
|
|
||||||
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
||||||
sqlx = { version = "0.7.4", features = [
|
sqlx = { version = "0.8.3", features = [
|
||||||
"sqlite",
|
"sqlite",
|
||||||
# "postgres",
|
# "postgres",
|
||||||
# "any",
|
# "any",
|
||||||
"macros",
|
"macros",
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
"tls-rustls",
|
"tls-rustls",
|
||||||
|
"time"
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,26 @@ go 1.21.9
|
||||||
|
|
||||||
require github.com/hashicorp/vault-client-go v0.4.3
|
require github.com/hashicorp/vault-client-go v0.4.3
|
||||||
|
|
||||||
|
require github.com/hashicorp/vault/api v1.16.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/crypto v0.32.0 // indirect
|
||||||
|
golang.org/x/net v0.34.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,77 @@
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||||
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc=
|
github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc=
|
||||||
github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY=
|
github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY=
|
||||||
|
github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
|
||||||
|
github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/vault-client-go"
|
// "github.com/hashicorp/vault-client-go"
|
||||||
"github.com/hashicorp/vault-client-go/schema"
|
// "github.com/hashicorp/vault-client-go/schema"
|
||||||
|
vault "github.com/hashicorp/vault/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var client *vault.Client
|
var client *vault.Client
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|
||||||
// Apparently used as a default if mountpath is an empty string (client library)
|
// Apparently used as a default if mountpath is an empty string (client library)
|
||||||
var mountpath = "/kv-v2"
|
var mountpath = "/kv-v2"
|
||||||
var mountpath2 = "/some"
|
var mountpath2 = "/some"
|
||||||
|
|
@ -20,93 +23,86 @@ var mountpath2 = "/some"
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
config := vault.DefaultConfig()
|
||||||
|
config.Address = "http://localhost:8200"
|
||||||
|
config.Timeout = 30*time.Second
|
||||||
|
|
||||||
// prepare a client with the given base address
|
// prepare a client with the given base address
|
||||||
client, err = vault.New(
|
client, err = vault.NewClient(config)
|
||||||
vault.WithAddress("http://localhost:8200"),
|
|
||||||
vault.WithRequestTimeout(30*time.Second),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("unable to initialize Vault client: %v", err)
|
||||||
}
|
}
|
||||||
log.Println("client prepared")
|
log.Println("client prepared")
|
||||||
|
|
||||||
// authenticate with a root token (insecure)
|
// authenticate with a root token (insecure)
|
||||||
if err := client.SetToken("my-token"); err != nil {
|
client.SetToken("my-token")
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode := m.Run() // run all tests and get code
|
exitCode := m.Run() // run all tests and get code
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func kv2Write(t *testing.T, mount string, path string) {
|
||||||
|
data := map[string]any{
|
||||||
|
"password1": "123abc",
|
||||||
|
"password2": "horse horse horse battery staple correct",
|
||||||
|
}
|
||||||
|
t.Logf("Attempting to write to KV2 %s path %s:\t", mount, path)
|
||||||
|
v, err := client.KVv2(mount).Put(ctx, path, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ERROR writing secret:\n\t", err)
|
||||||
|
}
|
||||||
|
t.Log("Success (unchecked)\n\t", v)
|
||||||
|
|
||||||
|
res, err := client.KVv2(mount).Get(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ERROR checking/reading secret (request failed)\n\t", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(res.Data, data) {
|
||||||
|
t.Fatal("AAAAH", res.Data)
|
||||||
|
t.Fatalf("ERROR secret received does not match what was outght to be written.\n\tWritten: %s\n\tReceived: %s\n", data, res.Data)
|
||||||
|
// t.Fatal("\tWritten: ", newVar.Data)
|
||||||
|
// t.Fatal("\tReceived:", res.Data.Data)
|
||||||
|
}
|
||||||
|
t.Logf("SUCCESS writing to KV2 %s path %s\n", mount, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kv2Delete(t *testing.T, mount string, path string) {
|
||||||
|
err := client.KVv2(mount).Delete(ctx, path) // currently disregarding modifier options
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("ERROR deleting secret:\n\t", err)
|
||||||
|
}
|
||||||
|
res, err := client.KVv2(mount).Get(ctx, path)
|
||||||
|
if res != nil || err == nil {
|
||||||
|
t.Fatal("ERROR checking/reading secret (request failed)\n\t", res, err)
|
||||||
|
}
|
||||||
|
t.Logf("SUCCESS deleting KV2 secret %s path %s\n", mount, path)
|
||||||
|
}
|
||||||
|
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#create-update-secret
|
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#create-update-secret
|
||||||
// @Philip der Path steht in der KvV2Write Methode
|
// @Philip der Path steht in der KvV2Write Methode
|
||||||
func TestWriteSecret(t *testing.T) {
|
func TestWriteSecret(t *testing.T) {
|
||||||
// Path foo
|
// Path foo
|
||||||
_, err := client.Secrets.KvV2Write(ctx, "foo", schema.KvV2WriteRequest{
|
t.Logf("Writing to first KV2 engine at %s...", mountpath)
|
||||||
Data: map[string]any{
|
kv2Write(t, mountpath, "foo")
|
||||||
"password1": "123abc",
|
kv2Write(t, mountpath, "bar")
|
||||||
"password2": "horse horse horse battery staple correct",
|
t.Logf("Writing to second KV2 engine at %s...", mountpath2)
|
||||||
}},
|
kv2Write(t, mountpath2, "foo")
|
||||||
vault.WithMountPath(mountpath),
|
kv2Write(t, mountpath2, "bar")
|
||||||
)
|
t.Logf("Deleting...")
|
||||||
if err != nil {
|
kv2Delete(t, mountpath, "foo")
|
||||||
log.Fatal("kv2: Failed to write secret:\n\t", err)
|
|
||||||
}
|
|
||||||
log.Println("kv2: Tried to write Secret at foo at mountpath: ", mountpath)
|
|
||||||
|
|
||||||
// Path bar
|
|
||||||
_, err = client.Secrets.KvV2Write(ctx, "bar", schema.KvV2WriteRequest{
|
|
||||||
Data: map[string]any{
|
|
||||||
"password1": "abc123",
|
|
||||||
"password2": "correct horse battery staple",
|
|
||||||
}},
|
|
||||||
vault.WithMountPath(mountpath),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("kv2: Failed to write secret:\n\t", err)
|
|
||||||
}
|
|
||||||
log.Println("kv2: Tried to write Secret at foo at mountpath: ", mountpath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteSecret2(t *testing.T) {
|
// func TestDeleteSecret(t *testing.T) {
|
||||||
// Path foo
|
// _, err := client.Secrets.KvV2Delete(ctx, "foo") // currently disregarding modifier options
|
||||||
_, err := client.Secrets.KvV2Write(ctx, "foo", schema.KvV2WriteRequest{
|
// if err != nil {
|
||||||
Data: map[string]any{
|
// log.Fatal("kv2: Failed to delete secret:\n\t", err)
|
||||||
"password1": "123abc",
|
// }
|
||||||
"password2": "horse horse horse battery staple correct",
|
// }
|
||||||
}},
|
|
||||||
vault.WithMountPath(mountpath2),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("kv2: Failed to write secret:\n\t", err)
|
|
||||||
}
|
|
||||||
log.Println("kv2: Tried to write Secret at foo at mountpath: ", mountpath2)
|
|
||||||
|
|
||||||
// Path bar
|
// func TestReadSecret(t *testing.T) {
|
||||||
_, err = client.Secrets.KvV2Write(ctx, "bar", schema.KvV2WriteRequest{
|
// _, err := client.Secrets.KvV2Read(ctx, "bar")
|
||||||
Data: map[string]any{
|
// if err != nil {
|
||||||
"password1": "abc123",
|
// log.Fatal("kv2: Failed to read secret:\n\t", err)
|
||||||
"password2": "correct horse battery staple",
|
// }
|
||||||
}},
|
// }
|
||||||
vault.WithMountPath(mountpath2),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("kv2: Failed to write secret:\n\t", err)
|
|
||||||
}
|
|
||||||
log.Println("kv2: Tried to write Secret at foo at mountpath: ", mountpath2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteSecret(t *testing.T) {
|
|
||||||
_, err := client.Secrets.KvV2Delete(ctx, "foo") // currently disregarding modifier options
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("kv2: Failed to delete secret:\n\t", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadSecret(t *testing.T) {
|
|
||||||
_, err := client.Secrets.KvV2Read(ctx, "bar")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("kv2: Failed to read secret:\n\t", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
|
||||||
CREATE TABLE metadata (
|
CREATE TABLE kv2_metadata (
|
||||||
secret_path TEXT PRIMARY KEY NOT NULL,
|
engine_path TEXT NOT NULL,
|
||||||
|
secret_path TEXT NOT NULL,
|
||||||
|
|
||||||
cas_required INTEGER NOT NULL, -- no bool datatype in sqlite
|
cas_required INTEGER NOT NULL, -- no bool datatype in sqlite
|
||||||
created_time TIMESTAMP NOT NULL,
|
created_time TIMESTAMP NOT NULL,
|
||||||
|
|
@ -10,19 +11,20 @@ CREATE TABLE metadata (
|
||||||
-- current_version INTEGER NOT NULL,
|
-- current_version INTEGER NOT NULL,
|
||||||
-- oldest_version INTEGER NOT NULL,
|
-- oldest_version INTEGER NOT NULL,
|
||||||
updated_time TIMESTAMP NOT NULL,
|
updated_time TIMESTAMP NOT NULL,
|
||||||
custom_data TEXT
|
custom_data TEXT,
|
||||||
|
|
||||||
|
PRIMARY KEY (engine_path, secret_path)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE secret_versions (
|
CREATE TABLE kv2_secret_version (
|
||||||
secret_data TEXT NOT NULL,
|
engine_path TEXT NOT NULL,
|
||||||
|
|
||||||
created_time TIMESTAMP NOT NULL,
|
|
||||||
deletion_time TIMESTAMP,
|
|
||||||
|
|
||||||
version_number INTEGER NOT NULL DEFAULT 0,
|
|
||||||
secret_path TEXT NOT NULL,
|
secret_path TEXT NOT NULL,
|
||||||
PRIMARY KEY (secret_path, version_number),
|
|
||||||
FOREIGN KEY (secret_path) REFERENCES metadata(secret_path)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_secret_versions_secret_path ON secret_versions (secret_path);
|
version_number INTEGER NOT NULL,
|
||||||
|
secret_data TEXT NOT NULL,
|
||||||
|
created_time DATETIME NOT NULL,
|
||||||
|
deletion_time DATETIME,
|
||||||
|
|
||||||
|
PRIMARY KEY (engine_path, secret_path, version_number),
|
||||||
|
FOREIGN KEY (engine_path, secret_path) REFERENCES kv2_metadata(engine_path, secret_path)
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
-- Add migration script here
|
|
||||||
|
|
||||||
INSERT INTO metadata VALUES ("bar", false, DateTime('now'), "123", 4, DateTime('now'), "customData");
|
|
||||||
|
|
||||||
INSERT INTO secret_versions VALUES ("secret_data", DateTime('now'), DateTime('now'), 1, "bar");
|
|
||||||
|
|
@ -20,6 +20,9 @@ struct EngineMapperState {
|
||||||
kv_v2: Router,
|
kv_v2: Router,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct EnginePath(String);
|
||||||
|
|
||||||
/// Secret engine router
|
/// Secret engine router
|
||||||
pub fn secrets_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
pub fn secrets_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
||||||
// State containing the pool and engine routers
|
// State containing the pool and engine routers
|
||||||
|
|
@ -28,6 +31,7 @@ pub fn secrets_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
||||||
kv_v2: kv::kv_router(pool.clone()),
|
kv_v2: kv::kv_router(pool.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Problem solved via fallback route
|
||||||
Router::new().fallback(engine_handler).with_state(state)
|
Router::new().fallback(engine_handler).with_state(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +59,7 @@ async fn engine_handler(
|
||||||
async fn call_router(engine: Router, mount_path: String, mut req: Request) -> Response {
|
async fn call_router(engine: Router, mount_path: String, mut req: Request) -> Response {
|
||||||
let rui = req.uri().path().replace(&mount_path, "").parse().unwrap();
|
let rui = req.uri().path().replace(&mount_path, "").parse().unwrap();
|
||||||
*req.uri_mut() = rui;
|
*req.uri_mut() = rui;
|
||||||
|
let mount_path = EnginePath(mount_path);
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.layer(Extension(mount_path))
|
.layer(Extension(mount_path))
|
||||||
|
|
@ -70,7 +75,7 @@ fn unknown_engine(engine_type: String) -> impl IntoResponse {
|
||||||
error!("Engine type {} not implemented", engine_type);
|
error!("Engine type {} not implemented", engine_type);
|
||||||
HttpError::simple(
|
HttpError::simple(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
format!("Engine type {} not implemented", engine_type),
|
format!("Engine type {engine_type} not implemented"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,37 +7,41 @@ pub mod structs;
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod tests;
|
// mod tests;
|
||||||
|
|
||||||
use std::{collections::HashMap, convert::Infallible};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
common::HttpError,
|
||||||
engines::kv::{logic::body_to_json, structs::*},
|
engines::kv::{logic::body_to_json, structs::*},
|
||||||
storage::DatabaseDriver,
|
storage::DatabaseDriver,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
response::IntoResponse,
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, NoContent, Response},
|
||||||
routing::*,
|
routing::*,
|
||||||
Json, Router,
|
Extension, Json, Router,
|
||||||
};
|
};
|
||||||
use log::{info, error};
|
use log::{error, info, warn};
|
||||||
use sqlx::Row;
|
use time::UtcDateTime;
|
||||||
|
|
||||||
|
use super::EnginePath;
|
||||||
|
|
||||||
pub fn kv_router(pool: DatabaseDriver) -> Router {
|
pub fn kv_router(pool: DatabaseDriver) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/config", get(get_config))
|
.route("/config", get(get_config))
|
||||||
.route("/config", post(post_config))
|
.route("/config", post(post_config))
|
||||||
.route("/data/*path", get(get_data))
|
.route("/data/{*path}", get(get_data))
|
||||||
// .route("/:mount_path/data/*path/", get(get_data))
|
// .route("/:mount_path/data/*path/", get(get_data))
|
||||||
.route("/data/*path", post(post_data))
|
.route("/data/{*path}", post(post_data))
|
||||||
.route("/data/*path", delete(delete_data))
|
.route("/data/{*path}", put(post_data))
|
||||||
.route("/delete/*path", post(delete_path))
|
.route("/data/{*path}", delete(delete_data))
|
||||||
.route("/destroy/*path", post(destroy_path))
|
.route("/delete/{*path}", post(delete_path))
|
||||||
.route("/metadata/*path", get(get_meta))
|
.route("/destroy/{*path}", post(destroy_path))
|
||||||
|
.route("/metadata/{*path}", get(get_meta))
|
||||||
// .route("/:mount_path/metadata/*path/", get(get_meta))
|
// .route("/:mount_path/metadata/*path/", get(get_meta))
|
||||||
.route("/metadata/*path", post(post_meta))
|
.route("/metadata/{*path}", post(post_meta))
|
||||||
.route("/metadata/*path", delete(delete_meta))
|
.route("/metadata/{*path}", delete(delete_meta))
|
||||||
.route("/subkeys/*path", get(get_subkeys))
|
.route("/subkeys/{*path}", get(get_subkeys))
|
||||||
.route("/undelete/*path", post(post_undelete))
|
.route("/undelete/{*path}", post(post_undelete))
|
||||||
.with_state(pool)
|
.with_state(pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,88 +56,114 @@ async fn post_config() -> &'static str {
|
||||||
async fn get_data(
|
async fn get_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DatabaseDriver>,
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
) -> Result<impl IntoResponse, Infallible> {
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
match sqlx::query("SELECT * FROM secret_versions WHERE secret_path = $1")
|
) -> Result<Response, ()> {
|
||||||
.bind(path)
|
log::trace!("AAAAAAAAAAAAAAAAAAAA! {path} of engine {engine_path}");
|
||||||
.fetch_one(&pool)
|
|
||||||
.await
|
match sqlx::query_as!(
|
||||||
|
KvSecretData,
|
||||||
|
r#"SELECT secret_data, created_time, deletion_time, version_number, secret_path
|
||||||
|
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
|
ORDER BY version_number DESC LIMIT 1"#,
|
||||||
|
engine_path, path
|
||||||
|
)
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => {
|
Ok(secret_content) => {
|
||||||
let version: i64 = v.get("version_number");
|
// let version: i64 = v.get("version_number");
|
||||||
let secret_content: HashMap<String, String> = HashMap::from([
|
// let secret_content: HashMap<String, String> = HashMap::from([
|
||||||
// TODO: use sqlx to parse the row to a struct, do not do it manually
|
// // TODO: use sqlx to parse the row to a struct, do not do it manually
|
||||||
("secret_data".to_string(), v.get("secret_data")),
|
// ("secret_data".to_string(), v.get("secret_data")),
|
||||||
("created_time".to_string(), v.get("created_time")),
|
// ("created_time".to_string(), v.get("created_time")),
|
||||||
("deletion_time".to_string(), v.get("deletion_time")),
|
// ("deletion_time".to_string(), v.get("deletion_time")),
|
||||||
("version_number".to_string(), version.to_string()),
|
// ("version_number".to_string(), version.to_string()),
|
||||||
("secret_path".to_string(), v.get("secret_path")),
|
// ("secret_path".to_string(), v.get("secret_path")),
|
||||||
]);
|
// ]);
|
||||||
let return_secret = KvSecretReq {
|
|
||||||
data: secret_content,
|
let data = Wrapper {
|
||||||
options: None,
|
data: serde_json::from_str(&secret_content.secret_data).unwrap(),
|
||||||
};
|
};
|
||||||
|
let return_secret = KvSecretRes {
|
||||||
|
data,
|
||||||
|
options: None,
|
||||||
|
version: Some(secret_content.version_number)
|
||||||
|
};
|
||||||
|
let return_secret = Json(return_secret);
|
||||||
info!("{:?}", return_secret);
|
info!("{:?}", return_secret);
|
||||||
|
|
||||||
Ok(Json(return_secret))
|
Ok(return_secret.into_response())
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
sqlx::Error::RowNotFound => {
|
sqlx::Error::RowNotFound => {
|
||||||
error!("{:?}", e);
|
error!("Row not found {:?}", e);
|
||||||
let error_data: HashMap<String, String> = HashMap::from([("error".to_string(), "Secret not found".to_string())]);
|
// let error_data: HashMap<String, String> =
|
||||||
let error_secret = KvSecretReq{data: error_data, options: None};
|
// HashMap::from([("error".to_string(), "Secret not found".to_string())]);
|
||||||
Ok(Json(error_secret))
|
// let error_secret = KvSecretRes {
|
||||||
},
|
// data: error_data,
|
||||||
_ => panic!("{:?}", e),
|
// options: None,
|
||||||
|
// };
|
||||||
|
Ok(HttpError::simple(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
"Secret not found within kv2 engine",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => panic!("{e:?}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_data(
|
async fn post_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DatabaseDriver>,
|
||||||
Path(path): Path<String>,
|
Path(kv_path): Path<String>,
|
||||||
body: String,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> &'static str {
|
Json(secret): Json<KvV2WriteRequest>,
|
||||||
// Insert Metadata first -> Else: Error because of foreign key constraint
|
) -> Result<Response, ()> {
|
||||||
|
log::debug!(
|
||||||
|
"Engine: {}, Secret: {}, Content: {}, Version: {:?}, path: {}",
|
||||||
|
engine_path,
|
||||||
|
kv_path,
|
||||||
|
secret.data,
|
||||||
|
secret.version, //.unwrap_or(0),
|
||||||
|
kv_path
|
||||||
|
);
|
||||||
|
|
||||||
// let mut body_json = body_to_json(body);
|
let created_time = time::UtcDateTime::now();
|
||||||
|
let ts = created_time.unix_timestamp();
|
||||||
|
|
||||||
// let secret: KvSecret = KvSecret {
|
let mut tx = pool.begin().await.unwrap();
|
||||||
// data: body_json["data"]["password1"].take().to_string(),
|
|
||||||
// version: body_json["data"]["version"].take().as_i64(),
|
|
||||||
// };
|
|
||||||
// log::debug!(
|
|
||||||
// "Secret: {}, Content: {}, Version: {}, path: {}",
|
|
||||||
// path,
|
|
||||||
// secret.data,
|
|
||||||
// secret.version.unwrap_or(0),
|
|
||||||
// path
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let created_time = "05-03-2024 12:00:00";
|
let res_m = sqlx::query!("
|
||||||
// let deletion_time = "05-03-2024 12:00:00";
|
INSERT INTO kv2_metadata (engine_path, secret_path, cas_required, created_time, max_versions, updated_time)
|
||||||
// let version = "0";
|
VALUES ($1, $2, 0, $3, 100, $3)
|
||||||
// match sqlx::query!(
|
ON CONFLICT(engine_path, secret_path) DO NOTHING;
|
||||||
// "INSERT INTO secret_versions VALUES ($1, $2, $3, $4, $5)",
|
", engine_path, kv_path, ts).execute(&mut *tx).await.unwrap();
|
||||||
// secret.data,
|
|
||||||
// created_time,
|
|
||||||
// deletion_time,
|
|
||||||
// version,
|
|
||||||
// version
|
|
||||||
// )
|
|
||||||
// .execute(&pool)
|
|
||||||
// .await
|
|
||||||
// {
|
|
||||||
// Ok(v) => {
|
|
||||||
// trace!("{:?}", v);
|
|
||||||
// "Success"
|
|
||||||
// }
|
|
||||||
// Err(e) => {
|
|
||||||
// trace!("{:?}", e);
|
|
||||||
// "Error"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
todo!("not implemented")
|
let res_r = sqlx::query_file!(
|
||||||
|
"src/engines/kv/post_secret.sql",
|
||||||
|
engine_path,
|
||||||
|
kv_path,
|
||||||
|
secret.data,
|
||||||
|
ts,
|
||||||
|
secret.version,
|
||||||
|
)
|
||||||
|
.fetch_one(&mut *tx)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
tx.commit().await.expect("FAILED TO WRITE TX!");
|
||||||
|
|
||||||
|
warn!("test: {res_m:?} {res_r:?} {}", res_r.version_number);
|
||||||
|
|
||||||
|
let res = KvV2WriteResponse {
|
||||||
|
created_time: created_time.into(),
|
||||||
|
custom_metadata: None,
|
||||||
|
deletion_time: None,
|
||||||
|
destroyed: false,
|
||||||
|
version: res_r.version_number,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(res).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mock for return
|
/* mock for return
|
||||||
|
|
@ -166,8 +196,70 @@ async fn post_data(
|
||||||
/// TODO: soft delete the secret version at path. can be undone with undelete_secret
|
/// TODO: soft delete the secret version at path. can be undone with undelete_secret
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
|
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
|
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
|
||||||
async fn delete_data() -> &'static str {
|
async fn delete_data(
|
||||||
todo!("not implemented")
|
State(pool): State<DatabaseDriver>,
|
||||||
|
Path(path): Path<String>,
|
||||||
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
|
) -> Result<Response, Response> {
|
||||||
|
log::debug!("Secret: {}, path: {}", path, path);
|
||||||
|
|
||||||
|
let del_time = UtcDateTime::now().unix_timestamp();
|
||||||
|
|
||||||
|
let mut tx = pool.begin().await.unwrap();
|
||||||
|
|
||||||
|
// TODO: Find a better way
|
||||||
|
let latest_version = sqlx::query!(
|
||||||
|
r#"
|
||||||
|
SELECT version_number AS latest_version FROM kv2_secret_version
|
||||||
|
WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
|
ORDER BY version_number DESC LIMIT 1"#,
|
||||||
|
engine_path,
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
.fetch_optional(&mut *tx)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let latest_version = match latest_version {
|
||||||
|
Some(v) => v.latest_version,
|
||||||
|
None => {
|
||||||
|
return Err(HttpError::simple(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
"No secret version found which could be deleted",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let u = sqlx::query!(
|
||||||
|
r#"
|
||||||
|
UPDATE kv2_secret_version
|
||||||
|
SET deletion_time = $4
|
||||||
|
WHERE engine_path = $1 AND secret_path = $2
|
||||||
|
AND version_number = $3
|
||||||
|
"#,
|
||||||
|
engine_path,
|
||||||
|
path,
|
||||||
|
latest_version,
|
||||||
|
del_time
|
||||||
|
)
|
||||||
|
.execute(&mut *tx)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Err(e) = u {
|
||||||
|
error!("Strange - a version to be deleted has been found but could not be found to set deletion.\n\t{e:?}");
|
||||||
|
// Not commited transactions will be aborted upon drop
|
||||||
|
// tx.rollback().await.unwrap();
|
||||||
|
return Err(HttpError::simple(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"A version to be deleted was found but could not be deleted",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.commit().await.unwrap();
|
||||||
|
|
||||||
|
info!("Secret {path} version {latest_version} of {engine_path} engine deleted! {u:?}");
|
||||||
|
|
||||||
|
Ok(NoContent.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_path() -> &'static str {
|
async fn delete_path() -> &'static str {
|
||||||
|
|
@ -187,7 +279,7 @@ async fn post_meta(
|
||||||
Path((mount_path, kv_path)): Path<(String, String)>,
|
Path((mount_path, kv_path)): Path<(String, String)>,
|
||||||
body: String,
|
body: String,
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
let mut body_json = body_to_json(body);
|
let body_json = body_to_json(body);
|
||||||
let meta_data: SecretMeta = Default::default();
|
let meta_data: SecretMeta = Default::default();
|
||||||
todo!("not implemented")
|
todo!("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/engines/kv/delete_secret.sql
Normal file
25
src/engines/kv/delete_secret.sql
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
WITH latest AS (
|
||||||
|
SELECT version_number AS version
|
||||||
|
FROM kv2_secret_version
|
||||||
|
WHERE engine_path = '/kv-v2' AND secret_path = 'foo' AND deletion_time IS NULL
|
||||||
|
ORDER BY version_number DESC
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
update_deleted AS (
|
||||||
|
UPDATE kv2_secret_version
|
||||||
|
SET deletion_time = CURRENT_TIMESTAMP
|
||||||
|
WHERE engine_path = '/kv-v2' AND secret_path = 'foo'
|
||||||
|
AND version_number = (SELECT version FROM latest)
|
||||||
|
RETURNING version_number AS deleted_version
|
||||||
|
),
|
||||||
|
new_latest AS (
|
||||||
|
SELECT version_number AS new_latest_version
|
||||||
|
FROM kv2_secret_version
|
||||||
|
WHERE engine_path = '/kv-v2' AND secret_path = 'foo' AND deletion_time IS NULL
|
||||||
|
ORDER BY version_number DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
(SELECT deleted_version FROM update_deleted) AS deleted_version,
|
||||||
|
(SELECT new_latest_version FROM new_latest) AS new_latest_version;
|
||||||
|
|
@ -6,13 +6,13 @@ use super::structs::*;
|
||||||
|
|
||||||
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
||||||
/// serialize secret to JSON String
|
/// serialize secret to JSON String
|
||||||
pub fn serialize_secret_json(secret: &KvSecretReq) -> Result<String, serde_json::Error> {
|
pub fn serialize_secret_json(secret: &KvSecretRes) -> Result<String, serde_json::Error> {
|
||||||
serde_json::to_string(&secret)
|
serde_json::to_string(&secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
||||||
/// deserialize JSON String to secret
|
/// deserialize JSON String to secret
|
||||||
pub fn deserialize_secret_struct(raw: &String) -> Result<KvSecretReq, serde_json::Error> {
|
pub fn deserialize_secret_struct(raw: &str) -> Result<KvSecretRes, serde_json::Error> {
|
||||||
serde_json::from_str(raw)
|
serde_json::from_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub fn serialize_metadata_json(secret: &SecretMeta) -> Result<String, serde_json
|
||||||
|
|
||||||
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
#[deprecated(note = "Use Axum functionality with structs instead, also, this should be inlined if it is actually needed")]
|
||||||
/// deserialize JSON String to metadata
|
/// deserialize JSON String to metadata
|
||||||
pub fn deserialize_metadata_struct(raw: &String) -> Result<SecretMeta, serde_json::Error> {
|
pub fn deserialize_metadata_struct(raw: &str) -> Result<SecretMeta, serde_json::Error> {
|
||||||
serde_json::from_str(raw)
|
serde_json::from_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
18
src/engines/kv/post_secret.sql
Normal file
18
src/engines/kv/post_secret.sql
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
WITH latest_version AS (
|
||||||
|
SELECT MAX(version_number) AS max_version
|
||||||
|
FROM kv2_secret_version
|
||||||
|
WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path
|
||||||
|
)
|
||||||
|
INSERT INTO kv2_secret_version (engine_path, secret_path, secret_data, created_time, version_number)
|
||||||
|
VALUES (
|
||||||
|
$1, -- engine_path
|
||||||
|
$2, -- secret_path
|
||||||
|
$3, -- secret_data
|
||||||
|
$4, -- created_time
|
||||||
|
CASE -- Use provided version if given
|
||||||
|
WHEN $5 IS NOT NULL THEN $5 -- version_number (optional)
|
||||||
|
ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 0)
|
||||||
|
END -- version_number logic
|
||||||
|
)
|
||||||
|
RETURNING version_number;
|
||||||
|
|
@ -1,46 +1,92 @@
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::FromRow;
|
||||||
use std::{collections::HashMap, vec};
|
use std::{collections::HashMap, vec};
|
||||||
|
use time::{OffsetDateTime, UtcDateTime, serde::rfc3339};
|
||||||
|
|
||||||
|
// #[derive(Serialize, Deserialize, Debug)]
|
||||||
|
// pub struct KvSecretData {
|
||||||
|
// pub secret_data: String,
|
||||||
|
// #[serde(with = "rfc3339")]
|
||||||
|
// pub created_time: UtcDateTime,
|
||||||
|
|
||||||
|
// #[serde(with = "rfc3339::option")]
|
||||||
|
// pub deletion_time: Option<UtcDateTime>,
|
||||||
|
// pub version_number: i64,
|
||||||
|
// pub secret_path: String,
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
/// For SQLite support
|
||||||
|
pub struct KvSecretData {
|
||||||
|
pub secret_data: String,
|
||||||
|
#[serde(with = "rfc3339")]
|
||||||
|
pub created_time: OffsetDateTime,
|
||||||
|
#[serde(with = "rfc3339::option")]
|
||||||
|
pub deletion_time: Option<OffsetDateTime>,
|
||||||
|
pub version_number: i64,
|
||||||
|
pub secret_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl From<KvSecretDataDBO> for KvSecretData {
|
||||||
|
// fn from(value: KvSecretDataDBO) -> Self {
|
||||||
|
// Self {
|
||||||
|
// secret_data: value.secret_data,
|
||||||
|
// created_time: value.created_time.to_offset(UtcOffset::UTC),
|
||||||
|
// deletion_time: value.deletion_time.map(|v| v.to_utc()),
|
||||||
|
// version_number: value.version_number,
|
||||||
|
// secret_path: value.secret_path,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Wrapper<T> {
|
||||||
|
pub data: T,
|
||||||
|
}
|
||||||
|
|
||||||
pub type KvSecretData = HashMap<String, String>;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct KvSecretReq {
|
pub struct KvSecretRes {
|
||||||
/// Map (required)
|
/// Map (required)
|
||||||
pub data: KvSecretData,
|
pub data: Wrapper<serde_json::Value>,
|
||||||
/// Map (optional), may contain `cas` integer
|
/// Map (optional), may contain `cas` integer
|
||||||
|
/// Set the `cas` value to use a Check-And-Set operation
|
||||||
// #[serde_as(as = "serde_with::EnumMap")]
|
// #[serde_as(as = "serde_with::EnumMap")]
|
||||||
pub options: Option<HashMap<String, String>>,
|
pub options: Option<HashMap<String, String>>,
|
||||||
// Version does not exist for create/update operations
|
// Version does not exist for create/update operations
|
||||||
// pub version: Option<i64>,
|
pub version: Option<i64>,
|
||||||
// TODO add all fields
|
// TODO add all fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct KvV2WriteRequest {
|
||||||
|
pub data: serde_json::Value,
|
||||||
|
pub options: Option<serde_json::Value>,
|
||||||
|
pub version: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct KvSecretResData {
|
pub struct KvV2WriteResponse {
|
||||||
pub created_time: DateTime<Utc>,
|
#[serde(with = "rfc3339")]
|
||||||
|
pub created_time: OffsetDateTime,
|
||||||
pub custom_metadata: Option<HashMap<String, String>>,
|
pub custom_metadata: Option<HashMap<String, String>>,
|
||||||
pub deletion_time: Option<DateTime<Utc>>,
|
#[serde(with = "rfc3339::option")]
|
||||||
|
pub deletion_time: Option<OffsetDateTime>,
|
||||||
pub destroyed: bool,
|
pub destroyed: bool,
|
||||||
pub version: i64,
|
pub version: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct KvSecretRes {
|
|
||||||
pub data: KvSecretResData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct VersionMeta {
|
pub struct VersionMeta {
|
||||||
pub created_time: DateTime<Utc>,
|
pub created_time: UtcDateTime,
|
||||||
pub deletion_time: Option<DateTime<Utc>>, // optional deletion time
|
pub deletion_time: Option<UtcDateTime>, // optional deletion time
|
||||||
pub destroyed: bool,
|
pub destroyed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct SecretMeta {
|
pub struct SecretMeta {
|
||||||
pub cas_required: bool,
|
pub cas_required: bool,
|
||||||
pub created_time: DateTime<Utc>,
|
pub created_time: UtcDateTime,
|
||||||
pub current_version: i64,
|
pub current_version: i64,
|
||||||
/// In Hashicorp:
|
/// In Hashicorp:
|
||||||
/// If not set, the backend's configured delete_version_after is used.
|
/// If not set, the backend's configured delete_version_after is used.
|
||||||
|
|
@ -50,7 +96,7 @@ pub struct SecretMeta {
|
||||||
// TODO https://developer.hashicorp.com/vault/docs/concepts/duration-format
|
// TODO https://developer.hashicorp.com/vault/docs/concepts/duration-format
|
||||||
pub max_versions: i64,
|
pub max_versions: i64,
|
||||||
pub oldest_version: i64,
|
pub oldest_version: i64,
|
||||||
pub updated_time: DateTime<Utc>,
|
pub updated_time: UtcDateTime,
|
||||||
/// User-provided key-value pairs that are used to describe arbitrary and version-agnostic information about a secret.
|
/// User-provided key-value pairs that are used to describe arbitrary and version-agnostic information about a secret.
|
||||||
pub custom_metadata: Option<HashMap<String, String>>,
|
pub custom_metadata: Option<HashMap<String, String>>,
|
||||||
pub versions: Vec<VersionMeta>,
|
pub versions: Vec<VersionMeta>,
|
||||||
|
|
@ -58,7 +104,7 @@ pub struct SecretMeta {
|
||||||
|
|
||||||
impl Default for SecretMeta {
|
impl Default for SecretMeta {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let current = Utc::now();
|
let current = UtcDateTime::now();
|
||||||
SecretMeta {
|
SecretMeta {
|
||||||
cas_required: false,
|
cas_required: false,
|
||||||
created_time: current,
|
created_time: current,
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,15 @@ mod sys;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
let _ = dotenvy::dotenv();
|
||||||
// To be configured via environment variables
|
// To be configured via environment variables
|
||||||
// choose from (highest to lowest): error, warn, info, debug, trace, off
|
// choose from (highest to lowest): error, warn, info, debug, trace, off
|
||||||
env::set_var("RUST_LOG", "trace"); // TODO: Remove to respect user configuration
|
// env::set_var("RUST_LOG", "trace"); // TODO: Remove to respect user configuration
|
||||||
// env::set_var("DATABASE_URL", "sqlite:test.db"); // TODO: move to .env
|
// env::set_var("DATABASE_URL", "sqlite:test.db"); // TODO: move to .env
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
// Listen on all IPv4 and IPv6 interfaces on port 8200 by default
|
// Listen on all IPv4 and IPv6 interfaces on port 8200 by default
|
||||||
let listen_addr = env::var("LISTEN_ADDR").unwrap_or("[::]:8200".to_string()); // Do not change
|
let listen_addr = env::var("LISTEN_ADDR").unwrap_or("[::]:8200".to_string());
|
||||||
let listen_addr = SocketAddr::from_str(&listen_addr).expect("Failed to parse LISTEN_ADDR");
|
let listen_addr = SocketAddr::from_str(&listen_addr).expect("Failed to parse LISTEN_ADDR");
|
||||||
|
|
||||||
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue