@wlfi-agent/cli 1.4.13 → 1.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +3968 -0
- package/Cargo.toml +50 -0
- package/README.md +426 -6
- package/crates/vault-cli-admin/Cargo.toml +26 -0
- package/crates/vault-cli-admin/src/io_utils.rs +500 -0
- package/crates/vault-cli-admin/src/main.rs +3990 -0
- package/crates/vault-cli-admin/src/shared_config.rs +624 -0
- package/crates/vault-cli-admin/src/tui/amounts.rs +180 -0
- package/crates/vault-cli-admin/src/tui/token_rpc.rs +250 -0
- package/crates/vault-cli-admin/src/tui/utils.rs +82 -0
- package/crates/vault-cli-admin/src/tui.rs +3410 -0
- package/crates/vault-cli-agent/Cargo.toml +24 -0
- package/crates/vault-cli-agent/src/io_utils.rs +576 -0
- package/crates/vault-cli-agent/src/main.rs +833 -0
- package/crates/vault-cli-daemon/Cargo.toml +28 -0
- package/crates/vault-cli-daemon/src/bin/wlfi-agent-system-keychain.rs +216 -0
- package/crates/vault-cli-daemon/src/main.rs +644 -0
- package/crates/vault-cli-daemon/src/relay_sync.rs +894 -0
- package/crates/vault-cli-daemon/tests/system_keychain_helper_acl.rs +167 -0
- package/crates/vault-daemon/Cargo.toml +32 -0
- package/crates/vault-daemon/src/daemon_parts/api_impl_and_utils.rs +1041 -0
- package/crates/vault-daemon/src/daemon_parts/core_helpers.rs +1256 -0
- package/crates/vault-daemon/src/daemon_parts/types_api_rpc.rs +622 -0
- package/crates/vault-daemon/src/lib.rs +54 -0
- package/crates/vault-daemon/src/persistence.rs +441 -0
- package/crates/vault-daemon/src/tests.rs +237 -0
- package/crates/vault-daemon/src/tests_parts/part1.rs +1224 -0
- package/crates/vault-daemon/src/tests_parts/part2.rs +1021 -0
- package/crates/vault-daemon/src/tests_parts/part3.rs +835 -0
- package/crates/vault-daemon/src/tests_parts/part4.rs +604 -0
- package/crates/vault-domain/Cargo.toml +20 -0
- package/crates/vault-domain/src/action.rs +849 -0
- package/crates/vault-domain/src/address.rs +51 -0
- package/crates/vault-domain/src/approval.rs +90 -0
- package/crates/vault-domain/src/constants.rs +4 -0
- package/crates/vault-domain/src/error.rs +54 -0
- package/crates/vault-domain/src/keys.rs +71 -0
- package/crates/vault-domain/src/lib.rs +42 -0
- package/crates/vault-domain/src/nonce.rs +102 -0
- package/crates/vault-domain/src/policy.rs +172 -0
- package/crates/vault-domain/src/request.rs +53 -0
- package/crates/vault-domain/src/scope.rs +24 -0
- package/crates/vault-domain/src/session.rs +50 -0
- package/crates/vault-domain/src/signature.rs +34 -0
- package/crates/vault-domain/src/tests.rs +651 -0
- package/crates/vault-domain/src/u128_as_decimal_string.rs +44 -0
- package/crates/vault-policy/Cargo.toml +17 -0
- package/crates/vault-policy/src/engine.rs +301 -0
- package/crates/vault-policy/src/error.rs +81 -0
- package/crates/vault-policy/src/lib.rs +17 -0
- package/crates/vault-policy/src/report.rs +34 -0
- package/crates/vault-policy/src/tests.rs +891 -0
- package/crates/vault-policy/src/tests_explain.rs +78 -0
- package/crates/vault-sdk-agent/Cargo.toml +21 -0
- package/crates/vault-sdk-agent/src/lib.rs +711 -0
- package/crates/vault-signer/Cargo.toml +25 -0
- package/crates/vault-signer/src/lib.rs +731 -0
- package/crates/vault-signer/tests/secure_enclave_acl.rs +54 -0
- package/crates/vault-transport-unix/Cargo.toml +24 -0
- package/crates/vault-transport-unix/src/lib.rs +1640 -0
- package/crates/vault-transport-xpc/Cargo.toml +25 -0
- package/crates/vault-transport-xpc/src/client_codec_api.rs +635 -0
- package/crates/vault-transport-xpc/src/lib.rs +680 -0
- package/crates/vault-transport-xpc/src/tests.rs +818 -0
- package/crates/vault-transport-xpc/tests/e2e_flow.rs +773 -0
- package/dist/cli.cjs +35088 -0
- package/dist/cli.cjs.map +1 -0
- package/package.json +45 -41
- package/packages/cache/.turbo/turbo-build.log +52 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs +43 -0
- package/packages/cache/dist/chunk-2QFWMUXT.cjs.map +1 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js +43 -0
- package/packages/cache/dist/chunk-4U63TZTQ.js.map +1 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs +404 -0
- package/packages/cache/dist/chunk-ALQ6H7KG.cjs.map +1 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js +404 -0
- package/packages/cache/dist/chunk-FGJEEF5N.js.map +1 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs +45 -0
- package/packages/cache/dist/chunk-UYNEHZHB.cjs.map +1 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js +45 -0
- package/packages/cache/dist/chunk-VXVMPG3W.js.map +1 -0
- package/packages/cache/dist/client/index.cjs +11 -0
- package/packages/cache/dist/client/index.cjs.map +1 -0
- package/packages/cache/dist/client/index.d.cts +15 -0
- package/packages/cache/dist/client/index.d.ts +15 -0
- package/packages/cache/dist/client/index.js +11 -0
- package/packages/cache/dist/client/index.js.map +1 -0
- package/packages/cache/dist/errors/index.cjs +11 -0
- package/packages/cache/dist/errors/index.cjs.map +1 -0
- package/packages/cache/dist/errors/index.d.cts +26 -0
- package/packages/cache/dist/errors/index.d.ts +26 -0
- package/packages/cache/dist/errors/index.js +11 -0
- package/packages/cache/dist/errors/index.js.map +1 -0
- package/packages/cache/dist/index.cjs +29 -0
- package/packages/cache/dist/index.cjs.map +1 -0
- package/packages/cache/dist/index.d.cts +4 -0
- package/packages/cache/dist/index.d.ts +4 -0
- package/packages/cache/dist/index.js +29 -0
- package/packages/cache/dist/index.js.map +1 -0
- package/packages/cache/dist/service/index.cjs +15 -0
- package/packages/cache/dist/service/index.cjs.map +1 -0
- package/packages/cache/dist/service/index.d.cts +184 -0
- package/packages/cache/dist/service/index.d.ts +184 -0
- package/packages/cache/dist/service/index.js +15 -0
- package/packages/cache/dist/service/index.js.map +1 -0
- package/packages/cache/node_modules/.bin/jiti +17 -0
- package/packages/cache/node_modules/.bin/tsc +17 -0
- package/packages/cache/node_modules/.bin/tsserver +17 -0
- package/packages/cache/node_modules/.bin/tsup +17 -0
- package/packages/cache/node_modules/.bin/tsup-node +17 -0
- package/packages/cache/node_modules/.bin/tsx +17 -0
- package/packages/cache/node_modules/.bin/vitest +17 -0
- package/packages/cache/package.json +48 -0
- package/packages/cache/src/client/index.ts +56 -0
- package/packages/cache/src/errors/index.ts +53 -0
- package/packages/cache/src/index.ts +3 -0
- package/packages/cache/src/service/index.test.ts +263 -0
- package/packages/cache/src/service/index.ts +678 -0
- package/packages/cache/tsconfig.json +13 -0
- package/packages/cache/tsup.config.ts +13 -0
- package/packages/cache/vitest.config.ts +16 -0
- package/packages/config/.turbo/turbo-build.log +18 -0
- package/packages/config/dist/index.cjs +1037 -0
- package/packages/config/dist/index.cjs.map +1 -0
- package/packages/config/dist/index.d.ts +131 -0
- package/packages/config/node_modules/.bin/jiti +17 -0
- package/packages/config/node_modules/.bin/tsc +17 -0
- package/packages/config/node_modules/.bin/tsserver +17 -0
- package/packages/config/node_modules/.bin/tsup +17 -0
- package/packages/config/node_modules/.bin/tsup-node +17 -0
- package/packages/config/node_modules/.bin/tsx +17 -0
- package/packages/config/package.json +21 -0
- package/packages/config/src/index.js +1 -0
- package/packages/config/src/index.ts +1282 -0
- package/packages/config/tsconfig.json +4 -0
- package/packages/rpc/.turbo/turbo-build.log +32 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs +3660 -0
- package/packages/rpc/dist/_esm-BCLXDO2R.cjs.map +1 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs +16 -0
- package/packages/rpc/dist/ccip-OWJLAW55.cjs.map +1 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs +6247 -0
- package/packages/rpc/dist/chunk-APQIFZ3B.cjs.map +1 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs +410 -0
- package/packages/rpc/dist/chunk-CDO2GWRD.cjs.map +1 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs +2249 -0
- package/packages/rpc/dist/chunk-QGTNTFJ7.cjs.map +1 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs +44 -0
- package/packages/rpc/dist/chunk-TZDTAHWR.cjs.map +1 -0
- package/packages/rpc/dist/index.cjs +7342 -0
- package/packages/rpc/dist/index.cjs.map +1 -0
- package/packages/rpc/dist/index.d.ts +3857 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs +18 -0
- package/packages/rpc/dist/secp256k1-WCNM675D.cjs.map +1 -0
- package/packages/rpc/node_modules/.bin/jiti +17 -0
- package/packages/rpc/node_modules/.bin/tsc +17 -0
- package/packages/rpc/node_modules/.bin/tsserver +17 -0
- package/packages/rpc/node_modules/.bin/tsup +17 -0
- package/packages/rpc/node_modules/.bin/tsup-node +17 -0
- package/packages/rpc/node_modules/.bin/tsx +17 -0
- package/packages/rpc/package.json +25 -0
- package/packages/rpc/src/index.ts +206 -0
- package/packages/rpc/tsconfig.json +4 -0
- package/packages/typescript/base.json +36 -0
- package/packages/typescript/nextjs.json +17 -0
- package/packages/typescript/package.json +10 -0
- package/packages/ui/.turbo/turbo-build.log +44 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js +11 -0
- package/packages/ui/dist/chunk-MOAFBKSA.js.map +1 -0
- package/packages/ui/dist/components/badge.d.ts +12 -0
- package/packages/ui/dist/components/badge.js +31 -0
- package/packages/ui/dist/components/badge.js.map +1 -0
- package/packages/ui/dist/components/button.d.ts +13 -0
- package/packages/ui/dist/components/button.js +40 -0
- package/packages/ui/dist/components/button.js.map +1 -0
- package/packages/ui/dist/components/card.d.ts +10 -0
- package/packages/ui/dist/components/card.js +39 -0
- package/packages/ui/dist/components/card.js.map +1 -0
- package/packages/ui/dist/components/input.d.ts +5 -0
- package/packages/ui/dist/components/input.js +28 -0
- package/packages/ui/dist/components/input.js.map +1 -0
- package/packages/ui/dist/components/label.d.ts +5 -0
- package/packages/ui/dist/components/label.js +13 -0
- package/packages/ui/dist/components/label.js.map +1 -0
- package/packages/ui/dist/components/separator.d.ts +5 -0
- package/packages/ui/dist/components/separator.js +13 -0
- package/packages/ui/dist/components/separator.js.map +1 -0
- package/packages/ui/dist/components/textarea.d.ts +5 -0
- package/packages/ui/dist/components/textarea.js +27 -0
- package/packages/ui/dist/components/textarea.js.map +1 -0
- package/packages/ui/dist/tailwind.d.ts +56 -0
- package/packages/ui/dist/tailwind.js +60 -0
- package/packages/ui/dist/tailwind.js.map +1 -0
- package/packages/ui/dist/utils/cn.d.ts +5 -0
- package/packages/ui/dist/utils/cn.js +7 -0
- package/packages/ui/dist/utils/cn.js.map +1 -0
- package/packages/ui/node_modules/.bin/jiti +17 -0
- package/packages/ui/node_modules/.bin/tsc +17 -0
- package/packages/ui/node_modules/.bin/tsserver +17 -0
- package/packages/ui/node_modules/.bin/tsup +17 -0
- package/packages/ui/node_modules/.bin/tsup-node +17 -0
- package/packages/ui/node_modules/.bin/tsx +17 -0
- package/packages/ui/package.json +69 -0
- package/packages/ui/src/components/badge.tsx +27 -0
- package/packages/ui/src/components/button.tsx +40 -0
- package/packages/ui/src/components/card.tsx +31 -0
- package/packages/ui/src/components/input.tsx +21 -0
- package/packages/ui/src/components/label.tsx +6 -0
- package/packages/ui/src/components/separator.tsx +6 -0
- package/packages/ui/src/components/textarea.tsx +20 -0
- package/packages/ui/src/globals.css +70 -0
- package/packages/ui/src/tailwind.ts +56 -0
- package/packages/ui/src/utils/cn.ts +6 -0
- package/packages/ui/tsconfig.json +20 -0
- package/packages/ui/tsup.config.ts +20 -0
- package/pnpm-workspace.yaml +4 -0
- package/scripts/install-rust-binaries.mjs +84 -0
- package/scripts/launchd/install-user-daemon.sh +358 -0
- package/scripts/launchd/run-vault-daemon.sh +5 -0
- package/scripts/launchd/run-wlfi-agent-daemon.sh +73 -0
- package/scripts/launchd/uninstall-user-daemon.sh +103 -0
- package/src/cli.ts +2121 -0
- package/src/lib/admin-guard.js +1 -0
- package/src/lib/admin-guard.ts +185 -0
- package/src/lib/admin-passthrough.ts +33 -0
- package/src/lib/admin-reset.ts +751 -0
- package/src/lib/admin-setup.ts +1612 -0
- package/src/lib/agent-auth-clear.js +1 -0
- package/src/lib/agent-auth-clear.ts +58 -0
- package/src/lib/agent-auth-forwarding.js +1 -0
- package/src/lib/agent-auth-forwarding.ts +149 -0
- package/src/lib/agent-auth-migrate.js +1 -0
- package/src/lib/agent-auth-migrate.ts +150 -0
- package/src/lib/agent-auth-revoke.ts +103 -0
- package/src/lib/agent-auth-rotate.ts +107 -0
- package/src/lib/agent-auth-token.js +1 -0
- package/src/lib/agent-auth-token.ts +25 -0
- package/src/lib/agent-auth.ts +89 -0
- package/src/lib/asset-broadcast.js +1 -0
- package/src/lib/asset-broadcast.ts +285 -0
- package/src/lib/bootstrap-artifacts.js +1 -0
- package/src/lib/bootstrap-artifacts.ts +205 -0
- package/src/lib/bootstrap-credentials.js +1 -0
- package/src/lib/bootstrap-credentials.ts +832 -0
- package/src/lib/config-amounts.js +1 -0
- package/src/lib/config-amounts.ts +189 -0
- package/src/lib/config-mutation.ts +27 -0
- package/src/lib/fs-trust.js +1 -0
- package/src/lib/fs-trust.ts +537 -0
- package/src/lib/keychain.js +1 -0
- package/src/lib/keychain.ts +225 -0
- package/src/lib/local-admin-access.ts +106 -0
- package/src/lib/network-selection.js +1 -0
- package/src/lib/network-selection.ts +71 -0
- package/src/lib/passthrough-security.js +1 -0
- package/src/lib/passthrough-security.ts +114 -0
- package/src/lib/rpc-guard.js +1 -0
- package/src/lib/rpc-guard.ts +7 -0
- package/src/lib/rust-spawn-options.js +1 -0
- package/src/lib/rust-spawn-options.ts +98 -0
- package/src/lib/rust.js +1 -0
- package/src/lib/rust.ts +143 -0
- package/src/lib/signed-tx.js +1 -0
- package/src/lib/signed-tx.ts +116 -0
- package/src/lib/status-repair-cli.ts +116 -0
- package/src/lib/sudo.js +1 -0
- package/src/lib/sudo.ts +172 -0
- package/src/lib/vault-password-forwarding.js +1 -0
- package/src/lib/vault-password-forwarding.ts +155 -0
- package/src/lib/wallet-profile.js +1 -0
- package/src/lib/wallet-profile.ts +332 -0
- package/src/lib/wallet-repair.js +1 -0
- package/src/lib/wallet-repair.ts +304 -0
- package/src/lib/wallet-setup.js +1 -0
- package/src/lib/wallet-setup.ts +1466 -0
- package/src/lib/wallet-status.js +1 -0
- package/src/lib/wallet-status.ts +640 -0
- package/tsconfig.base.json +17 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +25 -0
- package/turbo.json +41 -0
- package/LICENSE.md +0 -1
- package/dist/wlfa/index.cjs +0 -250
- package/dist/wlfa/index.d.cts +0 -1
- package/dist/wlfa/index.d.ts +0 -1
- package/dist/wlfa/index.js +0 -250
- package/dist/wlfc/index.cjs +0 -1839
- package/dist/wlfc/index.d.cts +0 -1
- package/dist/wlfc/index.d.ts +0 -1
- package/dist/wlfc/index.js +0 -1839
|
@@ -0,0 +1,1224 @@
|
|
|
1
|
+
#[tokio::test]
|
|
2
|
+
async fn daemon_enforces_per_tx_limit() {
|
|
3
|
+
let daemon = InMemoryDaemon::new(
|
|
4
|
+
"vault-password",
|
|
5
|
+
SoftwareSignerBackend::default(),
|
|
6
|
+
DaemonConfig::default(),
|
|
7
|
+
)
|
|
8
|
+
.expect("daemon");
|
|
9
|
+
|
|
10
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
11
|
+
let session = AdminSession {
|
|
12
|
+
vault_password: "vault-password".to_string(),
|
|
13
|
+
lease,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
daemon
|
|
17
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
18
|
+
.await
|
|
19
|
+
.expect("add policy");
|
|
20
|
+
|
|
21
|
+
let key = daemon
|
|
22
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
23
|
+
.await
|
|
24
|
+
.expect("key");
|
|
25
|
+
assert!(matches!(key.source, KeySource::Generated));
|
|
26
|
+
|
|
27
|
+
let agent_credentials = daemon
|
|
28
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
29
|
+
.await
|
|
30
|
+
.expect("agent");
|
|
31
|
+
|
|
32
|
+
let token = "0x1000000000000000000000000000000000000000"
|
|
33
|
+
.parse::<EvmAddress>()
|
|
34
|
+
.expect("token");
|
|
35
|
+
let recipient = "0x2000000000000000000000000000000000000000"
|
|
36
|
+
.parse::<EvmAddress>()
|
|
37
|
+
.expect("recipient");
|
|
38
|
+
let request = sign_request(
|
|
39
|
+
&agent_credentials,
|
|
40
|
+
AgentAction::Transfer {
|
|
41
|
+
chain_id: 1,
|
|
42
|
+
token,
|
|
43
|
+
to: recipient,
|
|
44
|
+
amount_wei: 101,
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
let result = daemon.sign_for_agent(request).await;
|
|
49
|
+
assert!(result.is_err());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[tokio::test]
|
|
53
|
+
async fn wrong_password_is_rejected() {
|
|
54
|
+
let daemon = InMemoryDaemon::new(
|
|
55
|
+
"vault-password",
|
|
56
|
+
SoftwareSignerBackend::default(),
|
|
57
|
+
DaemonConfig::default(),
|
|
58
|
+
)
|
|
59
|
+
.expect("daemon");
|
|
60
|
+
|
|
61
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
62
|
+
let bad_session = AdminSession {
|
|
63
|
+
vault_password: "wrong".to_string(),
|
|
64
|
+
lease,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let err = daemon
|
|
68
|
+
.add_policy(&bad_session, policy_all_per_tx(100))
|
|
69
|
+
.await
|
|
70
|
+
.expect_err("must reject bad session");
|
|
71
|
+
|
|
72
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[tokio::test]
|
|
76
|
+
async fn issue_lease_requires_correct_password() {
|
|
77
|
+
let daemon = InMemoryDaemon::new(
|
|
78
|
+
"vault-password",
|
|
79
|
+
SoftwareSignerBackend::default(),
|
|
80
|
+
DaemonConfig::default(),
|
|
81
|
+
)
|
|
82
|
+
.expect("daemon");
|
|
83
|
+
|
|
84
|
+
let err = daemon
|
|
85
|
+
.issue_lease("wrong-password")
|
|
86
|
+
.await
|
|
87
|
+
.expect_err("must reject bad password");
|
|
88
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#[tokio::test]
|
|
92
|
+
async fn oversized_admin_password_is_rejected() {
|
|
93
|
+
let daemon = InMemoryDaemon::new(
|
|
94
|
+
"vault-password",
|
|
95
|
+
SoftwareSignerBackend::default(),
|
|
96
|
+
DaemonConfig::default(),
|
|
97
|
+
)
|
|
98
|
+
.expect("daemon");
|
|
99
|
+
|
|
100
|
+
let err = daemon
|
|
101
|
+
.issue_lease(&"a".repeat((16 * 1024) + 1))
|
|
102
|
+
.await
|
|
103
|
+
.expect_err("must reject oversized password");
|
|
104
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#[tokio::test]
|
|
108
|
+
async fn repeated_failed_admin_password_attempts_trigger_temporary_lockout() {
|
|
109
|
+
let config = DaemonConfig {
|
|
110
|
+
max_failed_admin_auth_attempts: 2,
|
|
111
|
+
admin_auth_lockout: time::Duration::hours(1),
|
|
112
|
+
..DaemonConfig::default()
|
|
113
|
+
};
|
|
114
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
115
|
+
.expect("daemon");
|
|
116
|
+
|
|
117
|
+
let first = daemon
|
|
118
|
+
.issue_lease("wrong-password")
|
|
119
|
+
.await
|
|
120
|
+
.expect_err("first bad password must fail");
|
|
121
|
+
assert!(matches!(first, DaemonError::AuthenticationFailed));
|
|
122
|
+
|
|
123
|
+
let second = daemon
|
|
124
|
+
.issue_lease("wrong-password")
|
|
125
|
+
.await
|
|
126
|
+
.expect_err("second bad password must fail");
|
|
127
|
+
assert!(matches!(second, DaemonError::AuthenticationFailed));
|
|
128
|
+
|
|
129
|
+
let locked = daemon
|
|
130
|
+
.issue_lease("vault-password")
|
|
131
|
+
.await
|
|
132
|
+
.expect_err("lockout should reject even correct password until it expires");
|
|
133
|
+
assert!(matches!(locked, DaemonError::AuthenticationFailed));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[tokio::test]
|
|
137
|
+
async fn successful_admin_auth_resets_failed_password_counter() {
|
|
138
|
+
let config = DaemonConfig {
|
|
139
|
+
max_failed_admin_auth_attempts: 2,
|
|
140
|
+
admin_auth_lockout: time::Duration::hours(1),
|
|
141
|
+
..DaemonConfig::default()
|
|
142
|
+
};
|
|
143
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
144
|
+
.expect("daemon");
|
|
145
|
+
|
|
146
|
+
let first = daemon
|
|
147
|
+
.issue_lease("wrong-password")
|
|
148
|
+
.await
|
|
149
|
+
.expect_err("bad password must fail");
|
|
150
|
+
assert!(matches!(first, DaemonError::AuthenticationFailed));
|
|
151
|
+
|
|
152
|
+
daemon
|
|
153
|
+
.issue_lease("vault-password")
|
|
154
|
+
.await
|
|
155
|
+
.expect("correct password should reset failure counter");
|
|
156
|
+
|
|
157
|
+
let next = daemon
|
|
158
|
+
.issue_lease("wrong-password")
|
|
159
|
+
.await
|
|
160
|
+
.expect_err("later bad password must still fail without immediate lockout");
|
|
161
|
+
assert!(matches!(next, DaemonError::AuthenticationFailed));
|
|
162
|
+
|
|
163
|
+
daemon
|
|
164
|
+
.issue_lease("vault-password")
|
|
165
|
+
.await
|
|
166
|
+
.expect("single later failure should not lock out correct password");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#[tokio::test]
|
|
170
|
+
async fn create_agent_key_validates_policy_set() {
|
|
171
|
+
let daemon = InMemoryDaemon::new(
|
|
172
|
+
"vault-password",
|
|
173
|
+
SoftwareSignerBackend::default(),
|
|
174
|
+
DaemonConfig::default(),
|
|
175
|
+
)
|
|
176
|
+
.expect("daemon");
|
|
177
|
+
|
|
178
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
179
|
+
let session = AdminSession {
|
|
180
|
+
vault_password: "vault-password".to_string(),
|
|
181
|
+
lease,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
let key = daemon
|
|
185
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
186
|
+
.await
|
|
187
|
+
.expect("key");
|
|
188
|
+
|
|
189
|
+
let mut ids = BTreeSet::new();
|
|
190
|
+
ids.insert(Uuid::new_v4());
|
|
191
|
+
|
|
192
|
+
let err = daemon
|
|
193
|
+
.create_agent_key(&session, key.id, PolicyAttachment::PolicySet(ids))
|
|
194
|
+
.await
|
|
195
|
+
.expect_err("unknown policy must fail");
|
|
196
|
+
|
|
197
|
+
assert!(matches!(err, DaemonError::UnknownPolicy(_)));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#[tokio::test]
|
|
201
|
+
async fn create_agent_key_rejects_empty_policy_set() {
|
|
202
|
+
let daemon = InMemoryDaemon::new(
|
|
203
|
+
"vault-password",
|
|
204
|
+
SoftwareSignerBackend::default(),
|
|
205
|
+
DaemonConfig::default(),
|
|
206
|
+
)
|
|
207
|
+
.expect("daemon");
|
|
208
|
+
|
|
209
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
210
|
+
let session = AdminSession {
|
|
211
|
+
vault_password: "vault-password".to_string(),
|
|
212
|
+
lease,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
let key = daemon
|
|
216
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
217
|
+
.await
|
|
218
|
+
.expect("key");
|
|
219
|
+
|
|
220
|
+
let empty = std::collections::BTreeSet::new();
|
|
221
|
+
let err = daemon
|
|
222
|
+
.create_agent_key(&session, key.id, PolicyAttachment::PolicySet(empty))
|
|
223
|
+
.await
|
|
224
|
+
.expect_err("empty policy set must fail");
|
|
225
|
+
|
|
226
|
+
assert!(matches!(err, DaemonError::InvalidPolicyAttachment(_)));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
#[tokio::test]
|
|
230
|
+
async fn daemon_backfills_default_relay_url() {
|
|
231
|
+
let daemon = InMemoryDaemon::new(
|
|
232
|
+
"vault-password",
|
|
233
|
+
SoftwareSignerBackend::default(),
|
|
234
|
+
DaemonConfig::default(),
|
|
235
|
+
)
|
|
236
|
+
.expect("daemon");
|
|
237
|
+
|
|
238
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
239
|
+
let session = AdminSession {
|
|
240
|
+
vault_password: "vault-password".to_string(),
|
|
241
|
+
lease,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
let relay_config = daemon
|
|
245
|
+
.get_relay_config(&session)
|
|
246
|
+
.await
|
|
247
|
+
.expect("get relay config");
|
|
248
|
+
|
|
249
|
+
assert_eq!(
|
|
250
|
+
relay_config.relay_url.as_deref(),
|
|
251
|
+
Some("http://localhost:8787")
|
|
252
|
+
);
|
|
253
|
+
assert_eq!(relay_config.frontend_url, None);
|
|
254
|
+
assert!(!relay_config.daemon_id_hex.trim().is_empty());
|
|
255
|
+
assert!(!relay_config.daemon_public_key_hex.trim().is_empty());
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
#[tokio::test]
|
|
259
|
+
async fn set_relay_config_allows_loopback_http_and_remote_https() {
|
|
260
|
+
let daemon = InMemoryDaemon::new(
|
|
261
|
+
"vault-password",
|
|
262
|
+
SoftwareSignerBackend::default(),
|
|
263
|
+
DaemonConfig::default(),
|
|
264
|
+
)
|
|
265
|
+
.expect("daemon");
|
|
266
|
+
|
|
267
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
268
|
+
let session = AdminSession {
|
|
269
|
+
vault_password: "vault-password".to_string(),
|
|
270
|
+
lease,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
let relay_config = daemon
|
|
274
|
+
.set_relay_config(
|
|
275
|
+
&session,
|
|
276
|
+
Some("http://127.0.0.1:8787".to_string()),
|
|
277
|
+
Some("https://relay.example".to_string()),
|
|
278
|
+
)
|
|
279
|
+
.await
|
|
280
|
+
.expect("set relay config");
|
|
281
|
+
|
|
282
|
+
assert_eq!(
|
|
283
|
+
relay_config.relay_url.as_deref(),
|
|
284
|
+
Some("http://127.0.0.1:8787")
|
|
285
|
+
);
|
|
286
|
+
assert_eq!(
|
|
287
|
+
relay_config.frontend_url.as_deref(),
|
|
288
|
+
Some("https://relay.example")
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
#[tokio::test]
|
|
293
|
+
async fn set_relay_config_rejects_remote_http() {
|
|
294
|
+
let daemon = InMemoryDaemon::new(
|
|
295
|
+
"vault-password",
|
|
296
|
+
SoftwareSignerBackend::default(),
|
|
297
|
+
DaemonConfig::default(),
|
|
298
|
+
)
|
|
299
|
+
.expect("daemon");
|
|
300
|
+
|
|
301
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
302
|
+
let session = AdminSession {
|
|
303
|
+
vault_password: "vault-password".to_string(),
|
|
304
|
+
lease,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
let err = daemon
|
|
308
|
+
.set_relay_config(&session, Some("http://relay.example".to_string()), None)
|
|
309
|
+
.await
|
|
310
|
+
.expect_err("remote http relay URL must fail");
|
|
311
|
+
|
|
312
|
+
assert!(
|
|
313
|
+
matches!(err, DaemonError::InvalidRelayConfig(message) if message.contains("must use https unless it targets localhost or a loopback address"))
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#[tokio::test]
|
|
318
|
+
async fn set_relay_config_rejects_userinfo_query_or_fragment() {
|
|
319
|
+
let daemon = InMemoryDaemon::new(
|
|
320
|
+
"vault-password",
|
|
321
|
+
SoftwareSignerBackend::default(),
|
|
322
|
+
DaemonConfig::default(),
|
|
323
|
+
)
|
|
324
|
+
.expect("daemon");
|
|
325
|
+
|
|
326
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
327
|
+
let session = AdminSession {
|
|
328
|
+
vault_password: "vault-password".to_string(),
|
|
329
|
+
lease,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
let userinfo_err = daemon
|
|
333
|
+
.set_relay_config(
|
|
334
|
+
&session,
|
|
335
|
+
Some("https://admin:secret@relay.example".to_string()),
|
|
336
|
+
None,
|
|
337
|
+
)
|
|
338
|
+
.await
|
|
339
|
+
.expect_err("userinfo must fail");
|
|
340
|
+
assert!(
|
|
341
|
+
matches!(userinfo_err, DaemonError::InvalidRelayConfig(message) if message.contains("must not include embedded username or password"))
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
let query_err = daemon
|
|
345
|
+
.set_relay_config(
|
|
346
|
+
&session,
|
|
347
|
+
None,
|
|
348
|
+
Some("https://relay.example/ui?debug=1".to_string()),
|
|
349
|
+
)
|
|
350
|
+
.await
|
|
351
|
+
.expect_err("query must fail");
|
|
352
|
+
assert!(
|
|
353
|
+
matches!(query_err, DaemonError::InvalidRelayConfig(message) if message.contains("must not include a query string"))
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
let fragment_err = daemon
|
|
357
|
+
.set_relay_config(
|
|
358
|
+
&session,
|
|
359
|
+
None,
|
|
360
|
+
Some("https://relay.example/ui#approval".to_string()),
|
|
361
|
+
)
|
|
362
|
+
.await
|
|
363
|
+
.expect_err("fragment must fail");
|
|
364
|
+
assert!(
|
|
365
|
+
matches!(fragment_err, DaemonError::InvalidRelayConfig(message) if message.contains("must not include a fragment"))
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#[tokio::test]
|
|
370
|
+
async fn rpc_roundtrip_for_issue_lease() {
|
|
371
|
+
let daemon = InMemoryDaemon::new(
|
|
372
|
+
"vault-password",
|
|
373
|
+
SoftwareSignerBackend::default(),
|
|
374
|
+
DaemonConfig::default(),
|
|
375
|
+
)
|
|
376
|
+
.expect("daemon");
|
|
377
|
+
|
|
378
|
+
let response = daemon
|
|
379
|
+
.handle_rpc(DaemonRpcRequest::IssueLease {
|
|
380
|
+
vault_password: "vault-password".to_string(),
|
|
381
|
+
})
|
|
382
|
+
.await
|
|
383
|
+
.expect("rpc must succeed");
|
|
384
|
+
|
|
385
|
+
match response {
|
|
386
|
+
DaemonRpcResponse::Lease(lease) => {
|
|
387
|
+
assert!(lease.expires_at > lease.issued_at);
|
|
388
|
+
}
|
|
389
|
+
_ => panic!("unexpected rpc response"),
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
#[tokio::test]
|
|
394
|
+
async fn list_policies_requires_authenticated_session() {
|
|
395
|
+
let daemon = InMemoryDaemon::new(
|
|
396
|
+
"vault-password",
|
|
397
|
+
SoftwareSignerBackend::default(),
|
|
398
|
+
DaemonConfig::default(),
|
|
399
|
+
)
|
|
400
|
+
.expect("daemon");
|
|
401
|
+
|
|
402
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
403
|
+
let bad_session = AdminSession {
|
|
404
|
+
vault_password: "wrong".to_string(),
|
|
405
|
+
lease,
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
let err = daemon
|
|
409
|
+
.list_policies(&bad_session)
|
|
410
|
+
.await
|
|
411
|
+
.expect_err("must reject unauthenticated list request");
|
|
412
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
#[tokio::test]
|
|
416
|
+
async fn add_policy_rejects_invalid_policy_payloads() {
|
|
417
|
+
let daemon = InMemoryDaemon::new(
|
|
418
|
+
"vault-password",
|
|
419
|
+
SoftwareSignerBackend::default(),
|
|
420
|
+
DaemonConfig::default(),
|
|
421
|
+
)
|
|
422
|
+
.expect("daemon");
|
|
423
|
+
|
|
424
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
425
|
+
let session = AdminSession {
|
|
426
|
+
vault_password: "vault-password".to_string(),
|
|
427
|
+
lease,
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
let zero_max = SpendingPolicy {
|
|
431
|
+
id: Uuid::new_v4(),
|
|
432
|
+
priority: 0,
|
|
433
|
+
policy_type: PolicyType::PerTxMaxSpending,
|
|
434
|
+
min_amount_wei: None,
|
|
435
|
+
max_amount_wei: 0,
|
|
436
|
+
recipients: EntityScope::All,
|
|
437
|
+
assets: EntityScope::All,
|
|
438
|
+
networks: EntityScope::All,
|
|
439
|
+
enabled: true,
|
|
440
|
+
};
|
|
441
|
+
let err = daemon
|
|
442
|
+
.add_policy(&session, zero_max)
|
|
443
|
+
.await
|
|
444
|
+
.expect_err("zero max amount must be rejected");
|
|
445
|
+
assert!(matches!(err, DaemonError::InvalidPolicy(_)));
|
|
446
|
+
|
|
447
|
+
let empty_recipient_scope = SpendingPolicy {
|
|
448
|
+
id: Uuid::new_v4(),
|
|
449
|
+
priority: 0,
|
|
450
|
+
policy_type: PolicyType::PerTxMaxSpending,
|
|
451
|
+
min_amount_wei: None,
|
|
452
|
+
max_amount_wei: 1,
|
|
453
|
+
recipients: EntityScope::Set(BTreeSet::new()),
|
|
454
|
+
assets: EntityScope::All,
|
|
455
|
+
networks: EntityScope::All,
|
|
456
|
+
enabled: true,
|
|
457
|
+
};
|
|
458
|
+
let err = daemon
|
|
459
|
+
.add_policy(&session, empty_recipient_scope)
|
|
460
|
+
.await
|
|
461
|
+
.expect_err("empty recipient scope must be rejected");
|
|
462
|
+
assert!(matches!(err, DaemonError::InvalidPolicy(_)));
|
|
463
|
+
|
|
464
|
+
let empty_asset_scope = SpendingPolicy {
|
|
465
|
+
id: Uuid::new_v4(),
|
|
466
|
+
priority: 0,
|
|
467
|
+
policy_type: PolicyType::PerTxMaxSpending,
|
|
468
|
+
min_amount_wei: None,
|
|
469
|
+
max_amount_wei: 1,
|
|
470
|
+
recipients: EntityScope::All,
|
|
471
|
+
assets: EntityScope::Set(BTreeSet::new()),
|
|
472
|
+
networks: EntityScope::All,
|
|
473
|
+
enabled: true,
|
|
474
|
+
};
|
|
475
|
+
let err = daemon
|
|
476
|
+
.add_policy(&session, empty_asset_scope)
|
|
477
|
+
.await
|
|
478
|
+
.expect_err("empty asset scope must be rejected");
|
|
479
|
+
assert!(matches!(err, DaemonError::InvalidPolicy(_)));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
#[tokio::test]
|
|
483
|
+
async fn list_policies_is_deterministic_for_equal_priority() {
|
|
484
|
+
let daemon = InMemoryDaemon::new(
|
|
485
|
+
"vault-password",
|
|
486
|
+
SoftwareSignerBackend::default(),
|
|
487
|
+
DaemonConfig::default(),
|
|
488
|
+
)
|
|
489
|
+
.expect("daemon");
|
|
490
|
+
|
|
491
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
492
|
+
let session = AdminSession {
|
|
493
|
+
vault_password: "vault-password".to_string(),
|
|
494
|
+
lease,
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
let mut high_id = policy_all_per_tx(100);
|
|
498
|
+
high_id.priority = 7;
|
|
499
|
+
high_id.id = Uuid::parse_str("ffffffff-ffff-ffff-ffff-ffffffffffff").expect("uuid");
|
|
500
|
+
|
|
501
|
+
let mut low_id = policy_all_per_tx(100);
|
|
502
|
+
low_id.priority = 7;
|
|
503
|
+
low_id.id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").expect("uuid");
|
|
504
|
+
|
|
505
|
+
daemon
|
|
506
|
+
.add_policy(&session, high_id.clone())
|
|
507
|
+
.await
|
|
508
|
+
.expect("add high id");
|
|
509
|
+
daemon
|
|
510
|
+
.add_policy(&session, low_id.clone())
|
|
511
|
+
.await
|
|
512
|
+
.expect("add low id");
|
|
513
|
+
|
|
514
|
+
let listed = daemon.list_policies(&session).await.expect("list policies");
|
|
515
|
+
let equal_priority: Vec<Uuid> = listed
|
|
516
|
+
.into_iter()
|
|
517
|
+
.filter(|p| p.priority == 7)
|
|
518
|
+
.map(|p| p.id)
|
|
519
|
+
.collect();
|
|
520
|
+
assert_eq!(equal_priority, vec![low_id.id, high_id.id]);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
#[tokio::test]
|
|
524
|
+
async fn lease_capacity_is_enforced() {
|
|
525
|
+
let config = DaemonConfig {
|
|
526
|
+
max_active_leases: 1,
|
|
527
|
+
..DaemonConfig::default()
|
|
528
|
+
};
|
|
529
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
530
|
+
.expect("daemon");
|
|
531
|
+
|
|
532
|
+
daemon
|
|
533
|
+
.issue_lease("vault-password")
|
|
534
|
+
.await
|
|
535
|
+
.expect("first lease");
|
|
536
|
+
let err = daemon
|
|
537
|
+
.issue_lease("vault-password")
|
|
538
|
+
.await
|
|
539
|
+
.expect_err("second lease must fail at capacity");
|
|
540
|
+
assert!(matches!(err, DaemonError::TooManyActiveLeases));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
#[tokio::test]
|
|
544
|
+
async fn expired_leases_are_pruned_before_capacity_check() {
|
|
545
|
+
let config = DaemonConfig {
|
|
546
|
+
max_active_leases: 1,
|
|
547
|
+
..DaemonConfig::default()
|
|
548
|
+
};
|
|
549
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
550
|
+
.expect("daemon");
|
|
551
|
+
|
|
552
|
+
let now = time::OffsetDateTime::now_utc();
|
|
553
|
+
let expired_id = Uuid::new_v4();
|
|
554
|
+
daemon.leases.write().expect("leases write").insert(
|
|
555
|
+
expired_id,
|
|
556
|
+
Lease {
|
|
557
|
+
lease_id: expired_id,
|
|
558
|
+
issued_at: now - time::Duration::hours(2),
|
|
559
|
+
expires_at: now - time::Duration::hours(1),
|
|
560
|
+
},
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
let fresh = daemon
|
|
564
|
+
.issue_lease("vault-password")
|
|
565
|
+
.await
|
|
566
|
+
.expect("must prune and issue");
|
|
567
|
+
assert_eq!(daemon.leases.read().expect("leases read").len(), 1);
|
|
568
|
+
assert_eq!(
|
|
569
|
+
daemon
|
|
570
|
+
.leases
|
|
571
|
+
.read()
|
|
572
|
+
.expect("leases read")
|
|
573
|
+
.keys()
|
|
574
|
+
.next()
|
|
575
|
+
.copied(),
|
|
576
|
+
Some(fresh.lease_id)
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
#[tokio::test]
|
|
581
|
+
async fn not_yet_valid_leases_are_pruned_before_capacity_check() {
|
|
582
|
+
let config = DaemonConfig {
|
|
583
|
+
max_active_leases: 1,
|
|
584
|
+
..DaemonConfig::default()
|
|
585
|
+
};
|
|
586
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
587
|
+
.expect("daemon");
|
|
588
|
+
|
|
589
|
+
let now = time::OffsetDateTime::now_utc();
|
|
590
|
+
let future_id = Uuid::new_v4();
|
|
591
|
+
daemon.leases.write().expect("leases write").insert(
|
|
592
|
+
future_id,
|
|
593
|
+
Lease {
|
|
594
|
+
lease_id: future_id,
|
|
595
|
+
issued_at: now + time::Duration::hours(2),
|
|
596
|
+
expires_at: now + time::Duration::hours(3),
|
|
597
|
+
},
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
let fresh = daemon
|
|
601
|
+
.issue_lease("vault-password")
|
|
602
|
+
.await
|
|
603
|
+
.expect("must prune invalid future lease and issue");
|
|
604
|
+
assert_eq!(daemon.leases.read().expect("leases read").len(), 1);
|
|
605
|
+
assert_eq!(
|
|
606
|
+
daemon
|
|
607
|
+
.leases
|
|
608
|
+
.read()
|
|
609
|
+
.expect("leases read")
|
|
610
|
+
.keys()
|
|
611
|
+
.next()
|
|
612
|
+
.copied(),
|
|
613
|
+
Some(fresh.lease_id)
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
#[test]
|
|
618
|
+
fn daemon_new_rejects_invalid_runtime_limits() {
|
|
619
|
+
let config = DaemonConfig {
|
|
620
|
+
max_active_leases: 0,
|
|
621
|
+
..DaemonConfig::default()
|
|
622
|
+
};
|
|
623
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
624
|
+
assert!(
|
|
625
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
626
|
+
"zero lease capacity must be rejected"
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
let config = DaemonConfig {
|
|
630
|
+
max_sign_payload_bytes: 0,
|
|
631
|
+
..DaemonConfig::default()
|
|
632
|
+
};
|
|
633
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
634
|
+
assert!(
|
|
635
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
636
|
+
"zero payload cap must be rejected"
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
let config = DaemonConfig {
|
|
640
|
+
max_request_ttl: time::Duration::ZERO,
|
|
641
|
+
..DaemonConfig::default()
|
|
642
|
+
};
|
|
643
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
644
|
+
assert!(
|
|
645
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
646
|
+
"non-positive request ttl must be rejected"
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
let config = DaemonConfig {
|
|
650
|
+
nonce_reservation_ttl: time::Duration::ZERO,
|
|
651
|
+
..DaemonConfig::default()
|
|
652
|
+
};
|
|
653
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
654
|
+
assert!(
|
|
655
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
656
|
+
"non-positive nonce reservation ttl must be rejected"
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
let config = DaemonConfig {
|
|
660
|
+
max_failed_admin_auth_attempts: 0,
|
|
661
|
+
..DaemonConfig::default()
|
|
662
|
+
};
|
|
663
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
664
|
+
assert!(
|
|
665
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
666
|
+
"zero admin auth attempt budget must be rejected"
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
let config = DaemonConfig {
|
|
670
|
+
admin_auth_lockout: time::Duration::ZERO,
|
|
671
|
+
..DaemonConfig::default()
|
|
672
|
+
};
|
|
673
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
674
|
+
assert!(
|
|
675
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
676
|
+
"non-positive admin auth lockout must be rejected"
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
let config = DaemonConfig {
|
|
680
|
+
lease_ttl: time::Duration::ZERO,
|
|
681
|
+
..DaemonConfig::default()
|
|
682
|
+
};
|
|
683
|
+
let result = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config);
|
|
684
|
+
assert!(
|
|
685
|
+
matches!(result, Err(DaemonError::InvalidConfig(_))),
|
|
686
|
+
"non-positive lease ttl must be rejected"
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
#[tokio::test]
|
|
691
|
+
async fn issue_lease_fails_closed_when_ttl_overflows_timestamp() {
|
|
692
|
+
let config = DaemonConfig {
|
|
693
|
+
lease_ttl: time::Duration::MAX,
|
|
694
|
+
..DaemonConfig::default()
|
|
695
|
+
};
|
|
696
|
+
let daemon = InMemoryDaemon::new("vault-password", SoftwareSignerBackend::default(), config)
|
|
697
|
+
.expect("daemon should construct");
|
|
698
|
+
|
|
699
|
+
let err = daemon
|
|
700
|
+
.issue_lease("vault-password")
|
|
701
|
+
.await
|
|
702
|
+
.expect_err("overflowing ttl must fail safely");
|
|
703
|
+
assert!(matches!(err, DaemonError::InvalidConfig(_)));
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
#[tokio::test]
|
|
707
|
+
async fn client_requested_at_cannot_bypass_window_limits() {
|
|
708
|
+
let daemon = InMemoryDaemon::new(
|
|
709
|
+
"vault-password",
|
|
710
|
+
SoftwareSignerBackend::default(),
|
|
711
|
+
DaemonConfig::default(),
|
|
712
|
+
)
|
|
713
|
+
.expect("daemon");
|
|
714
|
+
|
|
715
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
716
|
+
let session = AdminSession {
|
|
717
|
+
vault_password: "vault-password".to_string(),
|
|
718
|
+
lease,
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
let policy = SpendingPolicy::new(
|
|
722
|
+
0,
|
|
723
|
+
PolicyType::DailyMaxSpending,
|
|
724
|
+
100,
|
|
725
|
+
EntityScope::All,
|
|
726
|
+
EntityScope::All,
|
|
727
|
+
EntityScope::All,
|
|
728
|
+
)
|
|
729
|
+
.expect("policy");
|
|
730
|
+
daemon
|
|
731
|
+
.add_policy(&session, policy)
|
|
732
|
+
.await
|
|
733
|
+
.expect("add policy");
|
|
734
|
+
|
|
735
|
+
let key = daemon
|
|
736
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
737
|
+
.await
|
|
738
|
+
.expect("key");
|
|
739
|
+
let agent_credentials = daemon
|
|
740
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
741
|
+
.await
|
|
742
|
+
.expect("agent");
|
|
743
|
+
|
|
744
|
+
let token = "0x3000000000000000000000000000000000000000"
|
|
745
|
+
.parse::<EvmAddress>()
|
|
746
|
+
.expect("token");
|
|
747
|
+
let recipient = "0x4000000000000000000000000000000000000000"
|
|
748
|
+
.parse::<EvmAddress>()
|
|
749
|
+
.expect("recipient");
|
|
750
|
+
|
|
751
|
+
let mut first = sign_request(
|
|
752
|
+
&agent_credentials,
|
|
753
|
+
AgentAction::Transfer {
|
|
754
|
+
chain_id: 1,
|
|
755
|
+
token: token.clone(),
|
|
756
|
+
to: recipient.clone(),
|
|
757
|
+
amount_wei: 60,
|
|
758
|
+
},
|
|
759
|
+
);
|
|
760
|
+
// Intentionally stale; daemon must not trust this for spend accounting.
|
|
761
|
+
first.requested_at = time::OffsetDateTime::now_utc() - time::Duration::seconds(30);
|
|
762
|
+
daemon.sign_for_agent(first).await.expect("first sign");
|
|
763
|
+
|
|
764
|
+
let mut second = sign_request(
|
|
765
|
+
&agent_credentials,
|
|
766
|
+
AgentAction::Transfer {
|
|
767
|
+
chain_id: 1,
|
|
768
|
+
token,
|
|
769
|
+
to: recipient,
|
|
770
|
+
amount_wei: 60,
|
|
771
|
+
},
|
|
772
|
+
);
|
|
773
|
+
second.requested_at = time::OffsetDateTime::now_utc() - time::Duration::seconds(30);
|
|
774
|
+
let err = daemon
|
|
775
|
+
.sign_for_agent(second)
|
|
776
|
+
.await
|
|
777
|
+
.expect_err("daily limit must reject second request");
|
|
778
|
+
|
|
779
|
+
assert!(matches!(err, DaemonError::Policy(_)));
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
#[tokio::test]
|
|
783
|
+
async fn wrong_agent_auth_token_is_rejected() {
|
|
784
|
+
let daemon = InMemoryDaemon::new(
|
|
785
|
+
"vault-password",
|
|
786
|
+
SoftwareSignerBackend::default(),
|
|
787
|
+
DaemonConfig::default(),
|
|
788
|
+
)
|
|
789
|
+
.expect("daemon");
|
|
790
|
+
|
|
791
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
792
|
+
let session = AdminSession {
|
|
793
|
+
vault_password: "vault-password".to_string(),
|
|
794
|
+
lease,
|
|
795
|
+
};
|
|
796
|
+
daemon
|
|
797
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
798
|
+
.await
|
|
799
|
+
.expect("add policy");
|
|
800
|
+
|
|
801
|
+
let key = daemon
|
|
802
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
803
|
+
.await
|
|
804
|
+
.expect("key");
|
|
805
|
+
let mut agent_credentials = daemon
|
|
806
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
807
|
+
.await
|
|
808
|
+
.expect("agent");
|
|
809
|
+
agent_credentials.auth_token = "wrong-token".to_string();
|
|
810
|
+
|
|
811
|
+
let token = "0x7000000000000000000000000000000000000000"
|
|
812
|
+
.parse::<EvmAddress>()
|
|
813
|
+
.expect("token");
|
|
814
|
+
let recipient = "0x8000000000000000000000000000000000000000"
|
|
815
|
+
.parse::<EvmAddress>()
|
|
816
|
+
.expect("recipient");
|
|
817
|
+
|
|
818
|
+
let err = daemon
|
|
819
|
+
.sign_for_agent(sign_request(
|
|
820
|
+
&agent_credentials,
|
|
821
|
+
AgentAction::Transfer {
|
|
822
|
+
chain_id: 1,
|
|
823
|
+
token,
|
|
824
|
+
to: recipient,
|
|
825
|
+
amount_wei: 1,
|
|
826
|
+
},
|
|
827
|
+
))
|
|
828
|
+
.await
|
|
829
|
+
.expect_err("must reject bad auth token");
|
|
830
|
+
|
|
831
|
+
assert!(matches!(err, DaemonError::AgentAuthenticationFailed));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
#[tokio::test]
|
|
835
|
+
async fn oversized_agent_auth_token_is_rejected() {
|
|
836
|
+
let daemon = InMemoryDaemon::new(
|
|
837
|
+
"vault-password",
|
|
838
|
+
SoftwareSignerBackend::default(),
|
|
839
|
+
DaemonConfig::default(),
|
|
840
|
+
)
|
|
841
|
+
.expect("daemon");
|
|
842
|
+
|
|
843
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
844
|
+
let session = AdminSession {
|
|
845
|
+
vault_password: "vault-password".to_string(),
|
|
846
|
+
lease,
|
|
847
|
+
};
|
|
848
|
+
daemon
|
|
849
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
850
|
+
.await
|
|
851
|
+
.expect("add policy");
|
|
852
|
+
|
|
853
|
+
let key = daemon
|
|
854
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
855
|
+
.await
|
|
856
|
+
.expect("key");
|
|
857
|
+
let mut agent_credentials = daemon
|
|
858
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
859
|
+
.await
|
|
860
|
+
.expect("agent");
|
|
861
|
+
agent_credentials.auth_token = "a".repeat((16 * 1024) + 1);
|
|
862
|
+
|
|
863
|
+
let token = "0x7000000000000000000000000000000000000000"
|
|
864
|
+
.parse::<EvmAddress>()
|
|
865
|
+
.expect("token");
|
|
866
|
+
let recipient = "0x8000000000000000000000000000000000000000"
|
|
867
|
+
.parse::<EvmAddress>()
|
|
868
|
+
.expect("recipient");
|
|
869
|
+
|
|
870
|
+
let err = daemon
|
|
871
|
+
.sign_for_agent(sign_request(
|
|
872
|
+
&agent_credentials,
|
|
873
|
+
AgentAction::Transfer {
|
|
874
|
+
chain_id: 1,
|
|
875
|
+
token,
|
|
876
|
+
to: recipient,
|
|
877
|
+
amount_wei: 1,
|
|
878
|
+
},
|
|
879
|
+
))
|
|
880
|
+
.await
|
|
881
|
+
.expect_err("must reject oversized auth token");
|
|
882
|
+
|
|
883
|
+
assert!(matches!(err, DaemonError::AgentAuthenticationFailed));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
#[tokio::test]
|
|
887
|
+
async fn replayed_request_id_is_rejected() {
|
|
888
|
+
let daemon = InMemoryDaemon::new(
|
|
889
|
+
"vault-password",
|
|
890
|
+
SoftwareSignerBackend::default(),
|
|
891
|
+
DaemonConfig::default(),
|
|
892
|
+
)
|
|
893
|
+
.expect("daemon");
|
|
894
|
+
|
|
895
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
896
|
+
let session = AdminSession {
|
|
897
|
+
vault_password: "vault-password".to_string(),
|
|
898
|
+
lease,
|
|
899
|
+
};
|
|
900
|
+
daemon
|
|
901
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
902
|
+
.await
|
|
903
|
+
.expect("add policy");
|
|
904
|
+
|
|
905
|
+
let key = daemon
|
|
906
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
907
|
+
.await
|
|
908
|
+
.expect("key");
|
|
909
|
+
let agent_credentials = daemon
|
|
910
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
911
|
+
.await
|
|
912
|
+
.expect("agent");
|
|
913
|
+
|
|
914
|
+
let request = sign_request(
|
|
915
|
+
&agent_credentials,
|
|
916
|
+
AgentAction::Transfer {
|
|
917
|
+
chain_id: 1,
|
|
918
|
+
token: "0x7100000000000000000000000000000000000000"
|
|
919
|
+
.parse()
|
|
920
|
+
.expect("token"),
|
|
921
|
+
to: "0x8100000000000000000000000000000000000000"
|
|
922
|
+
.parse()
|
|
923
|
+
.expect("recipient"),
|
|
924
|
+
amount_wei: 1,
|
|
925
|
+
},
|
|
926
|
+
);
|
|
927
|
+
|
|
928
|
+
daemon
|
|
929
|
+
.sign_for_agent(request.clone())
|
|
930
|
+
.await
|
|
931
|
+
.expect("first request should pass");
|
|
932
|
+
let err = daemon
|
|
933
|
+
.sign_for_agent(request)
|
|
934
|
+
.await
|
|
935
|
+
.expect_err("replayed request id must fail");
|
|
936
|
+
assert!(matches!(err, DaemonError::RequestReplayDetected));
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
#[tokio::test]
|
|
940
|
+
async fn expired_request_is_rejected() {
|
|
941
|
+
let daemon = InMemoryDaemon::new(
|
|
942
|
+
"vault-password",
|
|
943
|
+
SoftwareSignerBackend::default(),
|
|
944
|
+
DaemonConfig::default(),
|
|
945
|
+
)
|
|
946
|
+
.expect("daemon");
|
|
947
|
+
|
|
948
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
949
|
+
let session = AdminSession {
|
|
950
|
+
vault_password: "vault-password".to_string(),
|
|
951
|
+
lease,
|
|
952
|
+
};
|
|
953
|
+
daemon
|
|
954
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
955
|
+
.await
|
|
956
|
+
.expect("add policy");
|
|
957
|
+
|
|
958
|
+
let key = daemon
|
|
959
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
960
|
+
.await
|
|
961
|
+
.expect("key");
|
|
962
|
+
let agent_credentials = daemon
|
|
963
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
964
|
+
.await
|
|
965
|
+
.expect("agent");
|
|
966
|
+
|
|
967
|
+
let mut request = sign_request(
|
|
968
|
+
&agent_credentials,
|
|
969
|
+
AgentAction::Transfer {
|
|
970
|
+
chain_id: 1,
|
|
971
|
+
token: "0x7200000000000000000000000000000000000000"
|
|
972
|
+
.parse()
|
|
973
|
+
.expect("token"),
|
|
974
|
+
to: "0x8200000000000000000000000000000000000000"
|
|
975
|
+
.parse()
|
|
976
|
+
.expect("recipient"),
|
|
977
|
+
amount_wei: 1,
|
|
978
|
+
},
|
|
979
|
+
);
|
|
980
|
+
request.expires_at = time::OffsetDateTime::now_utc() - time::Duration::seconds(1);
|
|
981
|
+
|
|
982
|
+
let err = daemon
|
|
983
|
+
.sign_for_agent(request)
|
|
984
|
+
.await
|
|
985
|
+
.expect_err("expired request should fail");
|
|
986
|
+
assert!(matches!(err, DaemonError::RequestExpired));
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
#[tokio::test]
|
|
990
|
+
async fn future_requested_at_beyond_allowed_clock_skew_is_rejected() {
|
|
991
|
+
let daemon = InMemoryDaemon::new(
|
|
992
|
+
"vault-password",
|
|
993
|
+
SoftwareSignerBackend::default(),
|
|
994
|
+
DaemonConfig::default(),
|
|
995
|
+
)
|
|
996
|
+
.expect("daemon");
|
|
997
|
+
|
|
998
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
999
|
+
let session = AdminSession {
|
|
1000
|
+
vault_password: "vault-password".to_string(),
|
|
1001
|
+
lease,
|
|
1002
|
+
};
|
|
1003
|
+
daemon
|
|
1004
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
1005
|
+
.await
|
|
1006
|
+
.expect("add policy");
|
|
1007
|
+
|
|
1008
|
+
let key = daemon
|
|
1009
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
1010
|
+
.await
|
|
1011
|
+
.expect("key");
|
|
1012
|
+
let agent_credentials = daemon
|
|
1013
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
1014
|
+
.await
|
|
1015
|
+
.expect("agent");
|
|
1016
|
+
|
|
1017
|
+
let now = time::OffsetDateTime::now_utc();
|
|
1018
|
+
let mut request = sign_request(
|
|
1019
|
+
&agent_credentials,
|
|
1020
|
+
AgentAction::Transfer {
|
|
1021
|
+
chain_id: 1,
|
|
1022
|
+
token: "0x7300000000000000000000000000000000000000"
|
|
1023
|
+
.parse()
|
|
1024
|
+
.expect("token"),
|
|
1025
|
+
to: "0x8300000000000000000000000000000000000000"
|
|
1026
|
+
.parse()
|
|
1027
|
+
.expect("recipient"),
|
|
1028
|
+
amount_wei: 1,
|
|
1029
|
+
},
|
|
1030
|
+
);
|
|
1031
|
+
request.requested_at = now + time::Duration::seconds(31);
|
|
1032
|
+
request.expires_at = request.requested_at + time::Duration::minutes(2);
|
|
1033
|
+
|
|
1034
|
+
let err = daemon
|
|
1035
|
+
.sign_for_agent(request)
|
|
1036
|
+
.await
|
|
1037
|
+
.expect_err("future-dated request must fail");
|
|
1038
|
+
assert!(matches!(err, DaemonError::InvalidRequestTimestamps));
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
#[tokio::test]
|
|
1042
|
+
async fn request_ttl_longer_than_max_is_rejected() {
|
|
1043
|
+
let daemon = InMemoryDaemon::new(
|
|
1044
|
+
"vault-password",
|
|
1045
|
+
SoftwareSignerBackend::default(),
|
|
1046
|
+
DaemonConfig::default(),
|
|
1047
|
+
)
|
|
1048
|
+
.expect("daemon");
|
|
1049
|
+
|
|
1050
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
1051
|
+
let session = AdminSession {
|
|
1052
|
+
vault_password: "vault-password".to_string(),
|
|
1053
|
+
lease,
|
|
1054
|
+
};
|
|
1055
|
+
daemon
|
|
1056
|
+
.add_policy(&session, policy_all_per_tx(100))
|
|
1057
|
+
.await
|
|
1058
|
+
.expect("add policy");
|
|
1059
|
+
|
|
1060
|
+
let key = daemon
|
|
1061
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
1062
|
+
.await
|
|
1063
|
+
.expect("key");
|
|
1064
|
+
let agent_credentials = daemon
|
|
1065
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
1066
|
+
.await
|
|
1067
|
+
.expect("agent");
|
|
1068
|
+
|
|
1069
|
+
let now = time::OffsetDateTime::now_utc();
|
|
1070
|
+
let mut request = sign_request(
|
|
1071
|
+
&agent_credentials,
|
|
1072
|
+
AgentAction::Transfer {
|
|
1073
|
+
chain_id: 1,
|
|
1074
|
+
token: "0x7400000000000000000000000000000000000000"
|
|
1075
|
+
.parse()
|
|
1076
|
+
.expect("token"),
|
|
1077
|
+
to: "0x8400000000000000000000000000000000000000"
|
|
1078
|
+
.parse()
|
|
1079
|
+
.expect("recipient"),
|
|
1080
|
+
amount_wei: 1,
|
|
1081
|
+
},
|
|
1082
|
+
);
|
|
1083
|
+
request.requested_at = now;
|
|
1084
|
+
request.expires_at = now + time::Duration::minutes(6);
|
|
1085
|
+
|
|
1086
|
+
let err = daemon
|
|
1087
|
+
.sign_for_agent(request)
|
|
1088
|
+
.await
|
|
1089
|
+
.expect_err("overlong request ttl must fail");
|
|
1090
|
+
assert!(matches!(err, DaemonError::InvalidRequestTimestamps));
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
#[test]
|
|
1094
|
+
fn daemon_rejects_blank_admin_passwords_in_core_constructors() {
|
|
1095
|
+
let err = match InMemoryDaemon::new(
|
|
1096
|
+
" \t\n ",
|
|
1097
|
+
SoftwareSignerBackend::default(),
|
|
1098
|
+
DaemonConfig::default(),
|
|
1099
|
+
) {
|
|
1100
|
+
Ok(_) => panic!("blank admin password must be rejected"),
|
|
1101
|
+
Err(err) => err,
|
|
1102
|
+
};
|
|
1103
|
+
assert!(
|
|
1104
|
+
matches!(err, DaemonError::InvalidConfig(message) if message.contains("admin_password"))
|
|
1105
|
+
);
|
|
1106
|
+
|
|
1107
|
+
let state_path = unique_state_path("blank-admin-password");
|
|
1108
|
+
let err = match InMemoryDaemon::new_with_persistent_store(
|
|
1109
|
+
"\n\r\t",
|
|
1110
|
+
SoftwareSignerBackend::default(),
|
|
1111
|
+
DaemonConfig::default(),
|
|
1112
|
+
PersistentStoreConfig::new(state_path.clone()),
|
|
1113
|
+
) {
|
|
1114
|
+
Ok(_) => panic!("blank persistent-store admin password must be rejected"),
|
|
1115
|
+
Err(err) => err,
|
|
1116
|
+
};
|
|
1117
|
+
assert!(
|
|
1118
|
+
matches!(err, DaemonError::InvalidConfig(message) if message.contains("admin_password"))
|
|
1119
|
+
);
|
|
1120
|
+
assert!(
|
|
1121
|
+
!state_path.exists(),
|
|
1122
|
+
"persistent store must not be created for blank passwords"
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
#[test]
|
|
1127
|
+
fn daemon_rejects_oversized_admin_passwords_in_core_constructors() {
|
|
1128
|
+
let oversized = "a".repeat((16 * 1024) + 1);
|
|
1129
|
+
let err = match InMemoryDaemon::new(
|
|
1130
|
+
&oversized,
|
|
1131
|
+
SoftwareSignerBackend::default(),
|
|
1132
|
+
DaemonConfig::default(),
|
|
1133
|
+
) {
|
|
1134
|
+
Ok(_) => panic!("oversized admin password must be rejected"),
|
|
1135
|
+
Err(err) => err,
|
|
1136
|
+
};
|
|
1137
|
+
assert!(
|
|
1138
|
+
matches!(err, DaemonError::InvalidConfig(message) if message.contains("must not exceed"))
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
let state_path = unique_state_path("oversized-admin-password");
|
|
1142
|
+
let err = match InMemoryDaemon::new_with_persistent_store(
|
|
1143
|
+
&oversized,
|
|
1144
|
+
SoftwareSignerBackend::default(),
|
|
1145
|
+
DaemonConfig::default(),
|
|
1146
|
+
PersistentStoreConfig::new(state_path.clone()),
|
|
1147
|
+
) {
|
|
1148
|
+
Ok(_) => panic!("oversized persistent-store admin password must be rejected"),
|
|
1149
|
+
Err(err) => err,
|
|
1150
|
+
};
|
|
1151
|
+
assert!(
|
|
1152
|
+
matches!(err, DaemonError::InvalidConfig(message) if message.contains("must not exceed"))
|
|
1153
|
+
);
|
|
1154
|
+
assert!(
|
|
1155
|
+
!state_path.exists(),
|
|
1156
|
+
"persistent store must not be created for oversized passwords"
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
#[tokio::test]
|
|
1161
|
+
async fn privileged_admin_mutators_require_authenticated_session() {
|
|
1162
|
+
let daemon = InMemoryDaemon::new(
|
|
1163
|
+
"vault-password",
|
|
1164
|
+
SoftwareSignerBackend::default(),
|
|
1165
|
+
DaemonConfig::default(),
|
|
1166
|
+
)
|
|
1167
|
+
.expect("daemon");
|
|
1168
|
+
|
|
1169
|
+
let lease = daemon.issue_lease("vault-password").await.expect("lease");
|
|
1170
|
+
let session = AdminSession {
|
|
1171
|
+
vault_password: "vault-password".to_string(),
|
|
1172
|
+
lease: lease.clone(),
|
|
1173
|
+
};
|
|
1174
|
+
let bad_session = AdminSession {
|
|
1175
|
+
vault_password: "wrong".to_string(),
|
|
1176
|
+
lease,
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
let policy = policy_all_per_tx(100);
|
|
1180
|
+
daemon
|
|
1181
|
+
.add_policy(&session, policy.clone())
|
|
1182
|
+
.await
|
|
1183
|
+
.expect("add policy");
|
|
1184
|
+
|
|
1185
|
+
let key = daemon
|
|
1186
|
+
.create_vault_key(&session, KeyCreateRequest::Generate)
|
|
1187
|
+
.await
|
|
1188
|
+
.expect("key");
|
|
1189
|
+
|
|
1190
|
+
let agent_credentials = daemon
|
|
1191
|
+
.create_agent_key(&session, key.id, PolicyAttachment::AllPolicies)
|
|
1192
|
+
.await
|
|
1193
|
+
.expect("agent");
|
|
1194
|
+
|
|
1195
|
+
let err = daemon
|
|
1196
|
+
.disable_policy(&bad_session, policy.id)
|
|
1197
|
+
.await
|
|
1198
|
+
.expect_err("must reject disable_policy for bad session");
|
|
1199
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
1200
|
+
|
|
1201
|
+
let err = daemon
|
|
1202
|
+
.create_vault_key(&bad_session, KeyCreateRequest::Generate)
|
|
1203
|
+
.await
|
|
1204
|
+
.expect_err("must reject create_vault_key for bad session");
|
|
1205
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
1206
|
+
|
|
1207
|
+
let err = daemon
|
|
1208
|
+
.create_agent_key(&bad_session, key.id, PolicyAttachment::AllPolicies)
|
|
1209
|
+
.await
|
|
1210
|
+
.expect_err("must reject create_agent_key for bad session");
|
|
1211
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
1212
|
+
|
|
1213
|
+
let err = daemon
|
|
1214
|
+
.rotate_agent_auth_token(&bad_session, agent_credentials.agent_key.id)
|
|
1215
|
+
.await
|
|
1216
|
+
.expect_err("must reject rotate_agent_auth_token for bad session");
|
|
1217
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
1218
|
+
|
|
1219
|
+
let err = daemon
|
|
1220
|
+
.revoke_agent_key(&bad_session, agent_credentials.agent_key.id)
|
|
1221
|
+
.await
|
|
1222
|
+
.expect_err("must reject revoke_agent_key for bad session");
|
|
1223
|
+
assert!(matches!(err, DaemonError::AuthenticationFailed));
|
|
1224
|
+
}
|