praetorian-cli 2.4.3__tar.gz → 2.4.4__tar.gz

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.
Files changed (151) hide show
  1. {praetorian_cli-2.4.3/praetorian_cli.egg-info → praetorian_cli-2.4.4}/PKG-INFO +1 -1
  2. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/get.py +25 -7
  3. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/credentials.py +16 -3
  4. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_credentials.py +156 -0
  5. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4/praetorian_cli.egg-info}/PKG-INFO +1 -1
  6. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/setup.cfg +1 -1
  7. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/LICENSE +0 -0
  8. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/MANIFEST.in +0 -0
  9. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/README.md +0 -0
  10. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/__init__.py +0 -0
  11. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/__init__.py +0 -0
  12. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/access.py +0 -0
  13. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/add.py +0 -0
  14. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/aegis.py +0 -0
  15. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/agent.py +0 -0
  16. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/chariot.py +0 -0
  17. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/cli_decorators.py +0 -0
  18. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/configure.py +0 -0
  19. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/critfinder.py +0 -0
  20. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/delete.py +0 -0
  21. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/engagement.py +0 -0
  22. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/enrich.py +0 -0
  23. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/export.py +0 -0
  24. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/find.py +0 -0
  25. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/imports.py +0 -0
  26. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/link.py +0 -0
  27. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/list.py +0 -0
  28. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/report.py +0 -0
  29. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/run.py +0 -0
  30. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/script.py +0 -0
  31. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/search.py +0 -0
  32. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/ssh_utils.py +0 -0
  33. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/test.py +0 -0
  34. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/unlink.py +0 -0
  35. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/update.py +0 -0
  36. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/handlers/utils.py +0 -0
  37. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/main.py +0 -0
  38. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/runners/__init__.py +0 -0
  39. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/runners/local.py +0 -0
  40. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/scripts/__init__.py +0 -0
  41. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/scripts/commands/__init__.py +0 -0
  42. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
  43. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/scripts/utils.py +0 -0
  44. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/__init__.py +0 -0
  45. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/chariot.py +0 -0
  46. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/__init__.py +0 -0
  47. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/account_discovery.py +0 -0
  48. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/accounts.py +0 -0
  49. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/ad.py +0 -0
  50. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/aegis.py +0 -0
  51. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/agents.py +0 -0
  52. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/assets.py +0 -0
  53. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/attributes.py +0 -0
  54. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/capabilities.py +0 -0
  55. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/configurations.py +0 -0
  56. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/definitions.py +0 -0
  57. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/files.py +0 -0
  58. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/integrations.py +0 -0
  59. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/jobs.py +0 -0
  60. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/keys.py +0 -0
  61. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/preseeds.py +0 -0
  62. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/reports.py +0 -0
  63. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/risks.py +0 -0
  64. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/scanners.py +0 -0
  65. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/schedules.py +0 -0
  66. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/schema.py +0 -0
  67. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/search.py +0 -0
  68. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/seeds.py +0 -0
  69. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/settings.py +0 -0
  70. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/statistics.py +0 -0
  71. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/webhook.py +0 -0
  72. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/entities/webpage.py +0 -0
  73. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/guard.py +0 -0
  74. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/keychain.py +0 -0
  75. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/mcp_server.py +0 -0
  76. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/model/__init__.py +0 -0
  77. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/model/aegis.py +0 -0
  78. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/model/globals.py +0 -0
  79. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/model/query.py +0 -0
  80. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/model/utils.py +0 -0
  81. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/__init__.py +0 -0
  82. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/pytest.ini +0 -0
  83. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_access_aws.py +0 -0
  84. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_account.py +0 -0
  85. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_agent.py +0 -0
  86. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_asset.py +0 -0
  87. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_attribute.py +0 -0
  88. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_bulk.py +0 -0
  89. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
  90. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_configuration.py +0 -0
  91. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_conversation.py +0 -0
  92. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_credential_process.py +0 -0
  93. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_definition.py +0 -0
  94. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_export_cli.py +0 -0
  95. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_extend.py +0 -0
  96. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_file.py +0 -0
  97. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_job.py +0 -0
  98. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_key.py +0 -0
  99. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_local_runner.py +0 -0
  100. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_mcp.py +0 -0
  101. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_preseed.py +0 -0
  102. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_report.py +0 -0
  103. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_risk.py +0 -0
  104. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_run_cli.py +0 -0
  105. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_search.py +0 -0
  106. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_seed.py +0 -0
  107. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_setting.py +0 -0
  108. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_webhook.py +0 -0
  109. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_webpage.py +0 -0
  110. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/test_z_cli.py +0 -0
  111. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/ui_mocks.py +0 -0
  112. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/sdk/test/utils.py +0 -0
  113. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/__init__.py +0 -0
  114. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/__init__.py +0 -0
  115. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/account_selector.py +0 -0
  116. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
  117. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/cp.py +0 -0
  118. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/help.py +0 -0
  119. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/info.py +0 -0
  120. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/job.py +0 -0
  121. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/job_helpers.py +0 -0
  122. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/list.py +0 -0
  123. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/proxy.py +0 -0
  124. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/schedule.py +0 -0
  125. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/schedule_helpers.py +0 -0
  126. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/set.py +0 -0
  127. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
  128. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/constants.py +0 -0
  129. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/menu.py +0 -0
  130. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/theme.py +0 -0
  131. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/aegis/utils.py +0 -0
  132. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/__init__.py +0 -0
  133. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/__init__.py +0 -0
  134. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/accounts.py +0 -0
  135. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/context.py +0 -0
  136. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/data.py +0 -0
  137. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/marcus.py +0 -0
  138. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/reporting.py +0 -0
  139. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/search.py +0 -0
  140. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/commands/tools.py +0 -0
  141. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/console.py +0 -0
  142. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/context.py +0 -0
  143. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/console/renderer.py +0 -0
  144. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/conversation/__init__.py +0 -0
  145. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli/ui/conversation/textual_chat.py +0 -0
  146. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli.egg-info/SOURCES.txt +0 -0
  147. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli.egg-info/dependency_links.txt +0 -0
  148. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli.egg-info/entry_points.txt +0 -0
  149. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli.egg-info/requires.txt +0 -0
  150. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/praetorian_cli.egg-info/top_level.txt +0 -0
  151. {praetorian_cli-2.4.3 → praetorian_cli-2.4.4}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.4.3
3
+ Version: 2.4.4
4
4
  Summary: For interacting with the Guard API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -4,7 +4,7 @@ import click
4
4
 
5
5
  from praetorian_cli.handlers.chariot import chariot
6
6
  from praetorian_cli.handlers.cli_decorators import cli_handler, praetorian_only
7
- from praetorian_cli.handlers.utils import print_json
7
+ from praetorian_cli.handlers.utils import error, print_json
8
8
 
9
9
 
10
10
  @chariot.group()
@@ -259,31 +259,49 @@ def configuration(chariot, key):
259
259
 
260
260
  @get.command()
261
261
  @cli_handler
262
- @click.argument('credential_id', required=True)
262
+ @click.argument('credential_id', required=False, default='')
263
263
  @click.option('--category', default='env-integration', help='The category of the credential (e.g., integration, cloud)')
264
264
  @click.option('--type', default='default', help='The type of credential (e.g., aws, gcp, azure, static, ssh_key, json)')
265
265
  @click.option('--format', default='token', help='The format of the credential response')
266
+ @click.option('--resolution',
267
+ type=click.Choice(['by-target', 'from-parent'], case_sensitive=False),
268
+ default='by-target',
269
+ show_default=True,
270
+ help='How the broker should locate the credential. '
271
+ 'by-target: use the supplied CREDENTIAL_ID as-is. '
272
+ 'from-parent: walk DISCOVERED ancestors of --resource-key to find one with a matching credential.')
273
+ @click.option('--resource-key', default=None,
274
+ help='Asset/resource key to scope the lookup. Required when --resolution=from-parent.')
266
275
  @click.option('--parameters', nargs=2, multiple=True, help='Additional parameters, as --parameters key value')
267
- def credential(chariot, credential_id, category, type, format, parameters):
276
+ def credential(chariot, credential_id, category, type, format, resolution, resource_key, parameters):
268
277
  """ Get a specific credential
269
278
 
270
279
  Retrieve a specific credential using the credential broker.
271
280
 
272
281
  \b
273
282
  Argument:
274
- - CREDENTIAL_ID: the ID of the credential to retrieve
283
+ - CREDENTIAL_ID: the ID of the credential to retrieve. Required for
284
+ --resolution=by-target; optional for from-parent.
275
285
 
276
286
  \b
277
287
  Example usages:
278
288
  - guard get credential aws-prod --category integration --type aws --format json
279
289
  - guard get credential ssh-key-1 --category cloud --type ssh_key --format pem
290
+ - guard get credential --resolution from-parent --resource-key '#asset#example.com#1.2.3.4' --type aws
280
291
  """
281
-
292
+ if resolution == 'by-target' and not credential_id:
293
+ error('CREDENTIAL_ID is required when --resolution=by-target.')
294
+ if resolution == 'from-parent' and not resource_key:
295
+ error('--resource-key is required when --resolution=from-parent.')
296
+
282
297
  params = {}
283
298
  if parameters:
284
299
  params = {key: value for key, value in parameters}
285
-
286
- result = chariot.credentials.get(credential_id, category, type, [format], **params)
300
+
301
+ result = chariot.credentials.get(
302
+ credential_id, category, type, [format],
303
+ resolution=resolution, resource_key=resource_key, **params,
304
+ )
287
305
  output = chariot.credentials.format_output(result)
288
306
  click.echo(output)
289
307
 
@@ -72,11 +72,14 @@ class Credentials:
72
72
  """
73
73
  return self.api.search.by_key_prefix('#credential', offset=offset, pages=pages)
74
74
 
75
- def get(self, credential_id, category, type, format, **parameters):
75
+ def get(self, credential_id, category, type, format, resolution='by-target',
76
+ resource_key=None, **parameters):
76
77
  """
77
78
  Get a specific credential using the credential broker.
78
79
 
79
- :param credential_id: The ID of the credential to retrieve
80
+ :param credential_id: The ID of the credential to retrieve. Required for
81
+ resolution='by-target'; optional for 'from-parent' (broker walks the
82
+ graph if empty).
80
83
  :type credential_id: str
81
84
  :param category: The category of the credential ('integration', 'cloud', 'env-integration')
82
85
  :type category: str
@@ -84,6 +87,13 @@ class Credentials:
84
87
  :type type: str
85
88
  :param format: The format of the credential response ('token', 'file', 'env')
86
89
  :type format: str or list
90
+ :param resolution: How the broker should locate the credential. One of
91
+ 'by-target' (use credential_id as-is; default) or 'from-parent'
92
+ (walk DISCOVERED ancestors of resource_key).
93
+ :type resolution: str
94
+ :param resource_key: Asset/resource key the credential is scoped to.
95
+ Required when resolution='from-parent'.
96
+ :type resource_key: str or None
87
97
  :param parameters: Additional parameters required for the credential request (e.g., region, role_arn)
88
98
  :type parameters: dict
89
99
  :return: The processed credential response based on the requested format
@@ -101,8 +111,11 @@ class Credentials:
101
111
  'Category': category,
102
112
  'Type': type,
103
113
  'Format': broker_format,
104
- 'Parameters': parameters
114
+ 'Resolution': resolution,
115
+ 'Parameters': parameters,
105
116
  }
117
+ if resource_key:
118
+ request['ResourceKey'] = resource_key
106
119
  response = self.api.post('broker', request)
107
120
  return self._process_credential_output(response, format)
108
121
 
@@ -9,6 +9,7 @@ from click.testing import CliRunner
9
9
  # Click commands on the `chariot` group via decorators at import time.
10
10
  import praetorian_cli.handlers.add # noqa: F401
11
11
  import praetorian_cli.handlers.delete # noqa: F401
12
+ import praetorian_cli.handlers.get # noqa: F401
12
13
  import praetorian_cli.handlers.update # noqa: F401
13
14
  from praetorian_cli.handlers.chariot import chariot
14
15
  from praetorian_cli.sdk.entities.assets import Assets
@@ -46,6 +47,89 @@ class TestCredentialsAdd:
46
47
  })
47
48
 
48
49
 
50
+ class TestCredentialsGet:
51
+ def test_get_builds_broker_request_with_resolution_by_target(self):
52
+ """The broker rejects Get requests without Resolution (PR #5457). The
53
+ CLI always passes an explicit credential_id, so Resolution must be
54
+ 'by-target' — anything else either ignores the id (global) or requires
55
+ a ResourceKey the CLI doesn't have (from-parent)."""
56
+ api = MagicMock()
57
+ api.post.return_value = {'credentialValue': {'token': 'abc'}}
58
+ creds = Credentials(api=api)
59
+
60
+ creds.get(
61
+ credential_id='e699e73e-e371-4117-9192-e32147dfe98c',
62
+ category='env-integration',
63
+ type='aws',
64
+ format=['token'],
65
+ accountId='123456789012',
66
+ )
67
+
68
+ api.post.assert_called_once_with('broker', {
69
+ 'Operation': 'get',
70
+ 'CredentialID': 'e699e73e-e371-4117-9192-e32147dfe98c',
71
+ 'Category': 'env-integration',
72
+ 'Type': 'aws',
73
+ 'Format': ['token'],
74
+ 'Resolution': 'by-target',
75
+ 'Parameters': {'accountId': '123456789012'},
76
+ })
77
+
78
+ def test_get_rewrites_credential_process_format_to_token(self):
79
+ """`credential-process` is a client-side format — the broker sees
80
+ 'token' and the SDK assembles the AWS credential_process JSON from the
81
+ response."""
82
+ api = MagicMock()
83
+ api.post.return_value = {
84
+ 'credentialValue': {
85
+ 'accessKeyId': 'ASIA...',
86
+ 'secretAccessKey': 'secret',
87
+ 'sessionToken': 'session',
88
+ 'expiration': '2026-04-10T12:00:00Z',
89
+ }
90
+ }
91
+ creds = Credentials(api=api)
92
+
93
+ creds.get(
94
+ credential_id='abc-123',
95
+ category='env-integration',
96
+ type='aws',
97
+ format=['credential-process'],
98
+ )
99
+
100
+ sent = api.post.call_args.args[1]
101
+ assert sent['Format'] == ['token']
102
+ assert sent['Resolution'] == 'by-target'
103
+
104
+ def test_get_with_from_parent_sends_resource_key(self):
105
+ """from-parent: broker walks DISCOVERED ancestors of ResourceKey for a
106
+ matching credential. ResourceKey is required, CredentialID may be
107
+ empty."""
108
+ api = MagicMock()
109
+ api.post.return_value = {'ok': True}
110
+ creds = Credentials(api=api)
111
+
112
+ creds.get(
113
+ credential_id='',
114
+ category='env-integration',
115
+ type='aws',
116
+ format=['token'],
117
+ resolution='from-parent',
118
+ resource_key='#asset#example.com#1.2.3.4',
119
+ )
120
+
121
+ api.post.assert_called_once_with('broker', {
122
+ 'Operation': 'get',
123
+ 'CredentialID': '',
124
+ 'Category': 'env-integration',
125
+ 'Type': 'aws',
126
+ 'Format': ['token'],
127
+ 'Resolution': 'from-parent',
128
+ 'Parameters': {},
129
+ 'ResourceKey': '#asset#example.com#1.2.3.4',
130
+ })
131
+
132
+
49
133
  class TestCredentialsDelete:
50
134
  def test_delete_builds_broker_request(self):
51
135
  api = MagicMock()
@@ -105,6 +189,8 @@ def fake_sdk():
105
189
  sdk = MagicMock()
106
190
  sdk.credentials.add.return_value = {'ok': True}
107
191
  sdk.credentials.delete.return_value = {'ok': True}
192
+ sdk.credentials.get.return_value = {'credentialValue': {'token': 'abc'}}
193
+ sdk.credentials.format_output.side_effect = lambda r: str(r)
108
194
  sdk.assets.update.return_value = {'ok': True}
109
195
  return sdk
110
196
 
@@ -251,6 +337,76 @@ class TestDeleteCredential:
251
337
  )
252
338
 
253
339
 
340
+ class TestGetCredentialCLI:
341
+ """`guard get credential ...` — verifies the --resolution flag and its
342
+ interaction with CREDENTIAL_ID / --resource-key."""
343
+
344
+ def test_default_resolution_is_by_target(self, runner, fake_sdk):
345
+ result = _invoke(runner, fake_sdk, [
346
+ 'get', 'credential', 'cred-abc-123',
347
+ '--category', 'env-integration',
348
+ '--type', 'aws',
349
+ ])
350
+ assert result.exit_code == 0, result.output
351
+ fake_sdk.credentials.get.assert_called_once_with(
352
+ 'cred-abc-123', 'env-integration', 'aws', ['token'],
353
+ resolution='by-target', resource_key=None,
354
+ )
355
+
356
+ def test_explicit_by_target_passes_through(self, runner, fake_sdk):
357
+ result = _invoke(runner, fake_sdk, [
358
+ 'get', 'credential', 'cred-abc-123',
359
+ '--resolution', 'by-target',
360
+ '--type', 'aws',
361
+ ])
362
+ assert result.exit_code == 0, result.output
363
+ kwargs = fake_sdk.credentials.get.call_args.kwargs
364
+ assert kwargs['resolution'] == 'by-target'
365
+ assert kwargs['resource_key'] is None
366
+
367
+ def test_from_parent_requires_resource_key(self, runner, fake_sdk):
368
+ result = _invoke(runner, fake_sdk, [
369
+ 'get', 'credential',
370
+ '--resolution', 'from-parent',
371
+ '--type', 'aws',
372
+ ])
373
+ assert result.exit_code != 0
374
+ assert '--resource-key' in result.output
375
+ fake_sdk.credentials.get.assert_not_called()
376
+
377
+ def test_from_parent_with_resource_key(self, runner, fake_sdk):
378
+ result = _invoke(runner, fake_sdk, [
379
+ 'get', 'credential',
380
+ '--resolution', 'from-parent',
381
+ '--resource-key', '#asset#example.com#1.2.3.4',
382
+ '--type', 'aws',
383
+ ])
384
+ assert result.exit_code == 0, result.output
385
+ fake_sdk.credentials.get.assert_called_once_with(
386
+ '', 'env-integration', 'aws', ['token'],
387
+ resolution='from-parent',
388
+ resource_key='#asset#example.com#1.2.3.4',
389
+ )
390
+
391
+ def test_by_target_requires_credential_id(self, runner, fake_sdk):
392
+ result = _invoke(runner, fake_sdk, [
393
+ 'get', 'credential',
394
+ '--resolution', 'by-target',
395
+ '--type', 'aws',
396
+ ])
397
+ assert result.exit_code != 0
398
+ assert 'CREDENTIAL_ID' in result.output
399
+ fake_sdk.credentials.get.assert_not_called()
400
+
401
+ def test_invalid_resolution_value_is_rejected(self, runner, fake_sdk):
402
+ result = _invoke(runner, fake_sdk, [
403
+ 'get', 'credential', 'cred-abc-123',
404
+ '--resolution', 'nope',
405
+ ])
406
+ assert result.exit_code != 0
407
+ assert 'nope' in result.output or "'by-target'" in result.output
408
+
409
+
254
410
  class TestUpdateAssetSecret:
255
411
  def test_update_asset_with_secret_passes_through(self, runner, fake_sdk):
256
412
  result = _invoke(runner, fake_sdk, [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.4.3
3
+ Version: 2.4.4
4
4
  Summary: For interacting with the Guard API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = praetorian-cli
3
- version = 2.4.3
3
+ version = 2.4.4
4
4
  author = Praetorian
5
5
  author_email = support@praetorian.com
6
6
  description = For interacting with the Guard API
File without changes
File without changes