apigeecli 0.53.2__tar.gz → 0.53.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 (99) hide show
  1. {apigeecli-0.53.2 → apigeecli-0.53.4}/LICENSE +0 -0
  2. {apigeecli-0.53.2 → apigeecli-0.53.4}/NOTICE +0 -0
  3. {apigeecli-0.53.2/apigeecli.egg-info → apigeecli-0.53.4}/PKG-INFO +34 -13
  4. {apigeecli-0.53.2 → apigeecli-0.53.4}/README.rst +29 -11
  5. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/__init__.py +3 -3
  6. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/__main__.py +0 -0
  7. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apiproducts/__init__.py +0 -0
  8. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apiproducts/apiproducts.py +0 -0
  9. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apiproducts/commands.py +0 -0
  10. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apiproducts/serializer.py +0 -0
  11. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/__init__.py +0 -0
  12. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/api_puller_with_dependency_extraction.py +6 -6
  13. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/apis.py +0 -0
  14. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/commands.py +0 -0
  15. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/deploy.py +0 -0
  16. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/pull.py +0 -0
  17. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apis/serializer.py +0 -0
  18. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apps/__init__.py +0 -0
  19. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apps/apps.py +0 -0
  20. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apps/commands.py +0 -0
  21. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/apps/serializer.py +0 -0
  22. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/auth.py +40 -52
  23. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/backups/__init__.py +0 -0
  24. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/backups/backups.py +2 -2
  25. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/backups/commands.py +0 -0
  26. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/caches/__init__.py +0 -0
  27. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/caches/caches.py +0 -0
  28. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/caches/commands.py +0 -0
  29. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/caches/serializer.py +0 -0
  30. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/cls.py +0 -0
  31. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/configure/__init__.py +0 -0
  32. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/configure/commands.py +0 -0
  33. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/console.py +0 -0
  34. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/crypto.py +0 -0
  35. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/deployments/__init__.py +0 -0
  36. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/deployments/commands.py +0 -0
  37. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/deployments/deployments.py +0 -0
  38. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/deployments/serializer.py +0 -0
  39. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/developers/__init__.py +0 -0
  40. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/developers/commands.py +0 -0
  41. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/developers/developers.py +0 -0
  42. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/developers/serializer.py +0 -0
  43. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/encryption_utils.py +12 -14
  44. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/exceptions.py +0 -0
  45. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keystores/__init__.py +0 -0
  46. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keystores/commands.py +0 -0
  47. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keystores/keystores.py +0 -0
  48. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keystores/serializer.py +0 -0
  49. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keyvaluemaps/__init__.py +0 -0
  50. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keyvaluemaps/commands.py +0 -0
  51. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keyvaluemaps/keyvaluemaps.py +0 -0
  52. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/keyvaluemaps/serializer.py +0 -0
  53. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/maskconfigs/__init__.py +0 -0
  54. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/maskconfigs/commands.py +0 -0
  55. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/maskconfigs/maskconfigs.py +0 -0
  56. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/maskconfigs/serializer.py +0 -0
  57. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/permissions/__init__.py +0 -0
  58. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/permissions/commands.py +0 -0
  59. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/permissions/permissions.py +0 -0
  60. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/permissions/serializer.py +0 -0
  61. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/plugins/__init__.py +0 -0
  62. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/plugins/commands.py +0 -0
  63. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/prefix.py +0 -0
  64. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/references/__init__.py +0 -0
  65. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/references/commands.py +0 -0
  66. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/references/references.py +0 -0
  67. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/references/serializer.py +0 -0
  68. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/sharedflows/__init__.py +0 -0
  69. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/sharedflows/commands.py +0 -0
  70. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/sharedflows/serializer.py +0 -0
  71. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/sharedflows/sharedflows.py +0 -0
  72. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/silent.py +0 -0
  73. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/targetservers/__init__.py +0 -0
  74. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/targetservers/commands.py +0 -0
  75. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/targetservers/serializer.py +0 -0
  76. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/targetservers/targetservers.py +0 -0
  77. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/types.py +0 -0
  78. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/userroles/__init__.py +0 -0
  79. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/userroles/commands.py +0 -0
  80. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/userroles/userroles.py +0 -0
  81. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/utils.py +0 -0
  82. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/utils_init.py +0 -0
  83. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/verbose.py +0 -0
  84. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/virtualhosts/__init__.py +0 -0
  85. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/virtualhosts/commands.py +0 -0
  86. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/virtualhosts/serializer.py +0 -0
  87. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigee/virtualhosts/virtualhosts.py +0 -0
  88. {apigeecli-0.53.2 → apigeecli-0.53.4/apigeecli.egg-info}/PKG-INFO +34 -13
  89. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigeecli.egg-info/SOURCES.txt +4 -1
  90. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigeecli.egg-info/dependency_links.txt +0 -0
  91. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigeecli.egg-info/entry_points.txt +1 -0
  92. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigeecli.egg-info/requires.txt +3 -3
  93. {apigeecli-0.53.2 → apigeecli-0.53.4}/apigeecli.egg-info/top_level.txt +0 -0
  94. {apigeecli-0.53.2 → apigeecli-0.53.4}/setup.cfg +0 -0
  95. {apigeecli-0.53.2 → apigeecli-0.53.4}/setup.py +0 -0
  96. {apigeecli-0.53.2 → apigeecli-0.53.4}/tests/__init__.py +0 -0
  97. apigeecli-0.53.4/tests/test_auth.py +63 -0
  98. apigeecli-0.53.4/tests/test_echo.py +35 -0
  99. apigeecli-0.53.4/tests/test_encryption_utils.py +32 -0
File without changes
File without changes
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apigeecli
3
- Version: 0.53.2
4
- Summary: apigee-cli is an unofficial command-line interface tool designed to simplify the use of the Apigee Edge Management API
3
+ Version: 0.53.4
4
+ Summary: The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication.
5
5
  Home-page: https://github.com/darumatic/apigee-cli
6
6
  Author: Matthew Delotavo
7
7
  Author-email: matthew.t.delotavo@gmail.com
8
8
  License: Apache license 2.0
9
9
  Project-URL: Documentation, https://darumatic.github.io/apigee-cli/index.html
10
+ Platform: UNKNOWN
10
11
  Classifier: Development Status :: 4 - Beta
11
12
  Classifier: Environment :: Console
12
13
  Classifier: Intended Audience :: Developers
@@ -22,23 +23,23 @@ License-File: NOTICE
22
23
  apigee-cli
23
24
  ==========
24
25
 
25
- ⚠️ **Warning:** This project is no longer actively maintained and was created specifically for previous Darumatic clients.
26
-
27
26
  |License| |Python version| |Downloads|
28
27
 
29
- apigee-cli is a command-line interface tool designed to simplify the use of the Apigee Edge Management API.
30
- It provides a user-friendly experience with features such as SSO support.
28
+ .. warning::
29
+
30
+ This tool is no longer actively maintained.
31
+
32
+ The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication.
33
+
34
+ Initially developed for Darumatic clients, this project remains available as a working proof of concept, proven reliable in production CI/CD pipelines. The custom plugins feature will remain functional even if Apigee Edge APIs stop working.
31
35
 
32
- It can be used for general administrative tasks, as a developer package,
33
- and to support automation for common development tasks such as CI/CD.
36
+ I have created private forks for clients, where I continued to add features, fixes, and unit tests. However, I do not intend to maintain this public version. Feel free to explore and learn from the code.
34
37
 
35
- With the apigee-cli, you can manage your Apigee Edge credentials using environment variables, config files,
36
- or command-line arguments.
38
+ If you’re up for a challenge, try updating the code to work with Apigee X :)
37
39
 
38
- The tool is highly experimental and is not affiliated with Apigee or Google.
40
+ .. note::
39
41
 
40
- For more information on getting started with the apigee-cli or for troubleshooting help,
41
- refer to the documentation and links provided.
42
+ **apigee-cli** is highly experimental and is not affiliated with Apigee or Google.
42
43
 
43
44
  ------------
44
45
  Installation
@@ -144,6 +145,24 @@ We built and use the Apigee CLI to implement and distribute features that allow
144
145
  to manage CI/CD, perform self-service operations and promote our DevOps workflows
145
146
  which are difficult to perform using official tools.
146
147
 
148
+ -------------
149
+ Running Tests
150
+ -------------
151
+
152
+ This project uses `unittest` for testing its codebase. In order to run the tests, you will need to install the `coverage.py` tool. You can install it using pip:
153
+
154
+ .. code-block:: bash
155
+
156
+ pip3 install coverage
157
+
158
+ Once `coverage.py` is installed, you can run the tests using the `runtests` script:
159
+
160
+ .. code-block:: bash
161
+
162
+ ./runtests
163
+
164
+ This script will run all the tests in the `tests` directory and generate a coverage report.
165
+
147
166
  ------------
148
167
  Getting Help
149
168
  ------------
@@ -224,3 +243,5 @@ This is not an officially supported Google product.
224
243
 
225
244
  .. _`Apigee CI/CD Docker releases`: https://hub.docker.com/r/darumatic/apigee-cicd
226
245
 
246
+
247
+
@@ -2,23 +2,23 @@
2
2
  apigee-cli
3
3
  ==========
4
4
 
5
- ⚠️ **Warning:** This project is no longer actively maintained and was created specifically for previous Darumatic clients.
6
-
7
5
  |License| |Python version| |Downloads|
8
6
 
9
- apigee-cli is a command-line interface tool designed to simplify the use of the Apigee Edge Management API.
10
- It provides a user-friendly experience with features such as SSO support.
7
+ .. warning::
8
+
9
+ This tool is no longer actively maintained.
10
+
11
+ The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication.
11
12
 
12
- It can be used for general administrative tasks, as a developer package,
13
- and to support automation for common development tasks such as CI/CD.
13
+ Initially developed for Darumatic clients, this project remains available as a working proof of concept, proven reliable in production CI/CD pipelines. The custom plugins feature will remain functional even if Apigee Edge APIs stop working.
14
14
 
15
- With the apigee-cli, you can manage your Apigee Edge credentials using environment variables, config files,
16
- or command-line arguments.
15
+ I have created private forks for clients, where I continued to add features, fixes, and unit tests. However, I do not intend to maintain this public version. Feel free to explore and learn from the code.
17
16
 
18
- The tool is highly experimental and is not affiliated with Apigee or Google.
17
+ If you’re up for a challenge, try updating the code to work with Apigee X :)
19
18
 
20
- For more information on getting started with the apigee-cli or for troubleshooting help,
21
- refer to the documentation and links provided.
19
+ .. note::
20
+
21
+ **apigee-cli** is highly experimental and is not affiliated with Apigee or Google.
22
22
 
23
23
  ------------
24
24
  Installation
@@ -124,6 +124,24 @@ We built and use the Apigee CLI to implement and distribute features that allow
124
124
  to manage CI/CD, perform self-service operations and promote our DevOps workflows
125
125
  which are difficult to perform using official tools.
126
126
 
127
+ -------------
128
+ Running Tests
129
+ -------------
130
+
131
+ This project uses `unittest` for testing its codebase. In order to run the tests, you will need to install the `coverage.py` tool. You can install it using pip:
132
+
133
+ .. code-block:: bash
134
+
135
+ pip3 install coverage
136
+
137
+ Once `coverage.py` is installed, you can run the tests using the `runtests` script:
138
+
139
+ .. code-block:: bash
140
+
141
+ ./runtests
142
+
143
+ This script will run all the tests in the `tests` directory and generate a coverage report.
144
+
127
145
  ------------
128
146
  Getting Help
129
147
  ------------
@@ -6,9 +6,9 @@ from apigee import utils_init
6
6
 
7
7
  APP = "apigeecli"
8
8
  CMD = "apigee"
9
- __version__ = "0.53.2"
10
- description = "apigee-cli is an unofficial command-line interface tool designed to simplify the use of the Apigee Edge Management API"
11
- long_description = """apigee-cli is an unofficial command-line interface tool designed to simplify the use of the Apigee Edge Management API. It provides a user-friendly experience with features such as SSO support. It can be used for general administrative tasks, as a developer package, and to support automation for common development tasks such as CI/CD. With the apigee-cli, you can manage your Apigee Edge credentials using environment variables, config files, or command-line arguments. The tool is highly experimental and is not affiliated with Apigee or Google. For more information on getting started with the apigee-cli or for troubleshooting help, refer to the documentation and links provided."""
9
+ __version__ = "0.53.4"
10
+ description = "The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication."
11
+ long_description = """This tool is no longer actively maintained. Initially developed for Darumatic clients, this project remains available as a working proof of concept, proven reliable in production CI/CD pipelines. The custom plugins feature will remain functional even if Apigee Edge APIs stop working."""
12
12
 
13
13
  APIGEE_CLI_DIRECTORY = utils_init.join_path_components(Path.home(), ".apigee")
14
14
  APIGEE_CLI_PLUGINS_DIRECTORY = utils_init.join_path_components(APIGEE_CLI_DIRECTORY, "plugins")
@@ -19,14 +19,14 @@ class ApiPullerWithDependencyExtraction:
19
19
  def __init__(self, auth, org_name, revision_number, environment, working_directory=None):
20
20
  self.auth = auth
21
21
  self.org_name = org_name
22
- self._working_directory = working_directory
22
+ self.working_directory = working_directory
23
23
  self.revision_number = revision_number
24
24
  self.environment = environment
25
- self._keyvaluemaps_dir = "keyvaluemaps"
26
- self._targetservers_dir = "targetservers"
27
- self._caches_dir = "caches"
28
- self._apiproxy_dir = self._working_directory
29
- self.zip_file = str(Path(self._apiproxy_dir).with_suffix(".zip"))
25
+ self.keyvaluemaps_dir = "keyvaluemaps"
26
+ self.targetservers_dir = "targetservers"
27
+ self.caches_dir = "caches"
28
+ self.apiproxy_dir = self.working_directory
29
+ self.zip_file = str(Path(self.apiproxy_dir).with_suffix(".zip"))
30
30
 
31
31
  @property
32
32
  def working_directory(self):
@@ -37,21 +37,21 @@ def attach_is_token_option(func, profile):
37
37
  "--token/--no-token",
38
38
  default=is_token,
39
39
  show_default=True,
40
- help="specify to use oauth without MFA",
40
+ help="specify to use OAuth without MFA",
41
41
  )(func)
42
42
  elif is_token_envvar in (True, "True", "true", "1"):
43
43
  func = click.option(
44
44
  "--token/--no-token",
45
45
  default=is_token_envvar,
46
46
  show_default=True,
47
- help="specify to use oauth without MFA",
47
+ help="specify to use OAuth without MFA",
48
48
  )(func)
49
49
  else:
50
50
  func = click.option(
51
51
  "--token/--no-token",
52
52
  default=False,
53
53
  show_default=True,
54
- help="specify to use oauth without MFA",
54
+ help="specify to use OAuth without MFA",
55
55
  )(func)
56
56
  return func
57
57
 
@@ -222,75 +222,68 @@ def get_access_token_for_mfa(
222
222
  )
223
223
 
224
224
 
225
- def get_access_token(auth, username, password, oauth_url, post_headers, session):
225
+ def get_access_token(auth, username, password, oauth_url, post_headers, session, passcode):
226
226
  if auth.token or APIGEE_CLI_IS_MACHINE_USER:
227
227
  return get_access_token_for_token(auth, username, password, oauth_url, post_headers, session)
228
228
  elif auth.mfa_secret:
229
229
  return get_access_token_for_mfa(auth, username, password, oauth_url, post_headers, session)
230
230
  elif auth.zonename:
231
- return get_access_token_for_sso(auth, username, password, oauth_url, post_headers, session)
231
+ return get_access_token_for_sso(auth, username, password, oauth_url, post_headers, session, passcode)
232
232
 
233
233
 
234
234
  def get_access_token_for_sso(
235
- auth, username, password, oauth_url, post_headers, session
235
+ auth, username, password, oauth_url, post_headers, session, passcode
236
236
  ):
237
237
  refresh_token = validate_refresh_token(auth)
238
238
  oauth_url = APIGEE_ZONENAME_OAUTH_URL.format(zonename=auth.zonename)
239
239
  passcode_url = APIGEE_SAML_LOGIN_URL.format(zonename=auth.zonename)
240
+
240
241
  if not refresh_token:
241
- post_body = get_sso_access_token_parameters(passcode_url)
242
+ if not passcode:
243
+ passcode = get_sso_temporary_authentication_code(passcode_url)
244
+ post_body = f"passcode={passcode}&grant_type=password&response_type=token"
242
245
  else:
243
246
  # Should we notify users that the refresh token is being used to verify the access token?
244
247
  #console.echo("Refresh Token found, renewing access token with Refresh Token...")
245
248
  post_body = f"grant_type=refresh_token&refresh_token={refresh_token}"
246
249
 
247
250
  try:
248
- try:
249
- response_post = session.post(
250
- f"{oauth_url}", headers=post_headers, data=post_body
251
- )
252
- response_data = response_post.json()
253
- response_data["access_token"]
254
- # If we didn't have a refresh token previously, save the refresh token we just got.
255
- if not refresh_token:
256
- with open(APIGEE_CLI_REFRESH_TOKEN_FILE, "w") as f:
257
- f.write(response_data["refresh_token"])
258
- return response_data
259
- except KeyError:
260
- sys.exit("Temporary Authentication Code or Refresh Token is invalid. Please try again.")
251
+ response_post = session.post(
252
+ f"{oauth_url}", headers=post_headers, data=post_body
253
+ )
254
+ response_data = response_post.json()
255
+ response_data["access_token"]
256
+
257
+ # If we didn't have a refresh token previously, save the refresh token we just got.
258
+ if not refresh_token:
259
+ with open(APIGEE_CLI_REFRESH_TOKEN_FILE, "w") as f:
260
+ f.write(response_data["refresh_token"])
261
+ return response_data
262
+ except KeyError:
263
+ sys.exit("Temporary Authentication Code or Refresh Token is invalid. Please try again.")
261
264
  except ConnectionError as ce:
262
265
  console.echo(ce)
263
- except KeyError:
264
- pass
265
266
 
266
267
 
267
268
  def get_config_value(config_section, config_key):
268
- try:
269
- config = configparser.ConfigParser()
270
- config.read(APIGEE_CLI_CREDENTIALS_FILE)
271
- if config_section in config:
269
+ config = configparser.ConfigParser()
270
+ config.read(APIGEE_CLI_CREDENTIALS_FILE)
271
+ if config_section in config:
272
+ if config_key in config[config_section]:
272
273
  return config[config_section][config_key]
273
- except Exception:
274
- return
275
274
 
276
275
 
277
- def get_sso_access_token_parameters(passcode_url):
276
+ def get_sso_temporary_authentication_code(passcode_url):
278
277
  webbrowser.open(passcode_url)
279
- console.echo(
280
- "SSO authorization page has automatically been opened in your default browser."
281
- )
282
- console.echo(
283
- "Follow the instructions in the browser to complete this authorization request."
284
- )
285
- console.echo(
286
- f"""\nIf your browser did not automatically open, go to the following URL and sign in:\n\n{passcode_url}\n\nthen copy the Temporary Authentication Code.\n"""
287
- )
278
+ console.echo("SSO authorization page has automatically been opened in your default browser.")
279
+ console.echo("Follow the instructions in the browser to complete this authorization request.")
280
+ console.echo(f"""\nIf your browser did not automatically open, go to the following URL and sign in:\n\n{passcode_url}\n\nthen copy the Temporary Authentication Code.\n""")
288
281
 
289
282
  passcode = click.prompt("Please enter the Temporary Authentication Code")
290
- return f"passcode={passcode}&grant_type=password&response_type=token"
283
+ return passcode
291
284
 
292
285
 
293
- def retrieve_access_token(authentication, session=None):
286
+ def retrieve_access_token(authentication, passcode=None):
294
287
  oauth_url = APIGEE_OAUTH_URL
295
288
  username = authentication.username
296
289
  password = authentication.password
@@ -302,7 +295,7 @@ def retrieve_access_token(authentication, session=None):
302
295
  "Accept": "application/json;charset=utf-8",
303
296
  "Authorization": "Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0",
304
297
  }
305
- response_data = get_access_token(authentication, username, password, oauth_url, post_headers, session)
298
+ response_data = get_access_token(authentication, username, password, oauth_url, post_headers, session, passcode)
306
299
  try:
307
300
  return response_data["access_token"]
308
301
  except KeyError as error:
@@ -366,19 +359,18 @@ def auth():
366
359
  pass
367
360
 
368
361
 
369
- @auth.command(name="get-access-token", help="request a fresh access token")
362
+ @auth.command(name="get-access-token", help="Request a fresh access token")
370
363
  @common_auth_options
371
364
  @common_verbose_options
372
365
  @common_silent_options
366
+ @click.option('--passcode', help='Apigee SAML temporary authentication code to use when getting an Apigee access token (optional)')
373
367
  def get_access_token_command(
374
- username, password, mfa_secret, token, zonename, org, profile, **kwargs
368
+ username, password, mfa_secret, token, zonename, org, profile, passcode, **kwargs
375
369
  ):
376
- console.echo(
377
- retrieve_access_token(generate_authentication(username, password, mfa_secret, token, zonename))
378
- )
370
+ console.echo(retrieve_access_token(generate_authentication(username, password, mfa_secret, token, zonename), passcode))
379
371
 
380
372
 
381
- @auth.command(help="view the current access token")
373
+ @auth.command(help="View the current access token")
382
374
  @common_auth_options
383
375
  @common_verbose_options
384
376
  @common_silent_options
@@ -392,11 +384,7 @@ def view_access_token(
392
384
  console.echo(validate_access_token(authentication_object))
393
385
  else:
394
386
  # Show the user/password base64 basic auth value
395
- console.echo(
396
- base64.b64encode(
397
- f"{authentication_object.username}:{authentication_object.password}".encode()
398
- ).decode()
399
- )
387
+ console.echo(base64.b64encode(f"{authentication_object.username}:{authentication_object.password}".encode()).decode())
400
388
 
401
389
 
402
390
  @auth.command(help="Clear cached access token and refresh token")
@@ -396,7 +396,7 @@ class Backups:
396
396
  except HTTPError as e:
397
397
  self.handle_apiproduct_download_error(e, apiproduct)
398
398
  except Exception as e:
399
- self.handle_general_exception(type(e).__name__, apiproduct)
399
+ self.handle_general_exception(type(e).__name__, apiproduct)
400
400
  self.update_progress_bar(description="API Products")
401
401
 
402
402
  def get_apiproduct_content(self, apiproduct):
@@ -498,7 +498,7 @@ class Backups:
498
498
  except HTTPError as e:
499
499
  self.handle_developer_download_error(e, developer)
500
500
  except Exception as e:
501
- self.handle_general_exception(e, developer)
501
+ self.handle_general_exception(e, developer)
502
502
  self.update_progress_bar(description="Developers")
503
503
 
504
504
  def get_developer_details(self, developer):
File without changes
File without changes
File without changes
@@ -1,5 +1,4 @@
1
1
  import base64
2
-
3
2
  import gnupg
4
3
 
5
4
  ENCRYPTED_HEADER_BEGIN = "-----BEGIN ENCRYPTED APIGEE CLI MESSAGE-----"
@@ -8,22 +7,20 @@ ENCRYPTED_HEADER_END = "-----END ENCRYPTED APIGEE CLI MESSAGE-----"
8
7
 
9
8
  def encrypt_message_with_gpg(secret, message, encoded=True):
10
9
  gpg = gnupg.GPG()
11
- if encoded:
12
- return base64.b64encode(
13
- str(
14
- gpg.encrypt(
15
- message, symmetric="AES256", passphrase=secret, recipients=None
16
- )
17
- ).encode()
18
- ).decode()
19
- return str(
20
- gpg.encrypt(message, symmetric="AES256", passphrase=secret, recipients=None)
10
+ encrypted_message = gpg.encrypt(
11
+ message, symmetric="AES256", passphrase=secret, recipients=None
21
12
  )
13
+ if encoded:
14
+ return base64.b64encode(str(encrypted_message).encode()).decode()
15
+ return str(encrypted_message)
22
16
 
23
17
 
24
18
  def has_encrypted_header(message):
25
- return message.startswith(ENCRYPTED_HEADER_BEGIN) and message.endswith(
26
- ENCRYPTED_HEADER_END
19
+ if not isinstance(message, str):
20
+ return False
21
+ return (
22
+ message.startswith(ENCRYPTED_HEADER_BEGIN)
23
+ and message.endswith(ENCRYPTED_HEADER_END)
27
24
  )
28
25
 
29
26
 
@@ -33,5 +30,6 @@ def decrypt_message_with_gpg(secret, message, encoded=True):
33
30
  return ""
34
31
  message = message[len(ENCRYPTED_HEADER_BEGIN) : -len(ENCRYPTED_HEADER_END)]
35
32
  if encoded:
36
- return str(gpg.decrypt(base64.b64decode(message).decode(), passphrase=secret))
33
+ decoded_message = base64.b64decode(message).decode()
34
+ return str(gpg.decrypt(decoded_message, passphrase=secret))
37
35
  return str(gpg.decrypt(message, passphrase=secret))
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apigeecli
3
- Version: 0.53.2
4
- Summary: apigee-cli is an unofficial command-line interface tool designed to simplify the use of the Apigee Edge Management API
3
+ Version: 0.53.4
4
+ Summary: The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication.
5
5
  Home-page: https://github.com/darumatic/apigee-cli
6
6
  Author: Matthew Delotavo
7
7
  Author-email: matthew.t.delotavo@gmail.com
8
8
  License: Apache license 2.0
9
9
  Project-URL: Documentation, https://darumatic.github.io/apigee-cli/index.html
10
+ Platform: UNKNOWN
10
11
  Classifier: Development Status :: 4 - Beta
11
12
  Classifier: Environment :: Console
12
13
  Classifier: Intended Audience :: Developers
@@ -22,23 +23,23 @@ License-File: NOTICE
22
23
  apigee-cli
23
24
  ==========
24
25
 
25
- ⚠️ **Warning:** This project is no longer actively maintained and was created specifically for previous Darumatic clients.
26
-
27
26
  |License| |Python version| |Downloads|
28
27
 
29
- apigee-cli is a command-line interface tool designed to simplify the use of the Apigee Edge Management API.
30
- It provides a user-friendly experience with features such as SSO support.
28
+ .. warning::
29
+
30
+ This tool is no longer actively maintained.
31
+
32
+ The Apigee Edge command-line interface is an unofficial Python command-line tool built to simplify and automate Apigee Edge API usage, with support for SSO, MFA, and basic authentication.
33
+
34
+ Initially developed for Darumatic clients, this project remains available as a working proof of concept, proven reliable in production CI/CD pipelines. The custom plugins feature will remain functional even if Apigee Edge APIs stop working.
31
35
 
32
- It can be used for general administrative tasks, as a developer package,
33
- and to support automation for common development tasks such as CI/CD.
36
+ I have created private forks for clients, where I continued to add features, fixes, and unit tests. However, I do not intend to maintain this public version. Feel free to explore and learn from the code.
34
37
 
35
- With the apigee-cli, you can manage your Apigee Edge credentials using environment variables, config files,
36
- or command-line arguments.
38
+ If you’re up for a challenge, try updating the code to work with Apigee X :)
37
39
 
38
- The tool is highly experimental and is not affiliated with Apigee or Google.
40
+ .. note::
39
41
 
40
- For more information on getting started with the apigee-cli or for troubleshooting help,
41
- refer to the documentation and links provided.
42
+ **apigee-cli** is highly experimental and is not affiliated with Apigee or Google.
42
43
 
43
44
  ------------
44
45
  Installation
@@ -144,6 +145,24 @@ We built and use the Apigee CLI to implement and distribute features that allow
144
145
  to manage CI/CD, perform self-service operations and promote our DevOps workflows
145
146
  which are difficult to perform using official tools.
146
147
 
148
+ -------------
149
+ Running Tests
150
+ -------------
151
+
152
+ This project uses `unittest` for testing its codebase. In order to run the tests, you will need to install the `coverage.py` tool. You can install it using pip:
153
+
154
+ .. code-block:: bash
155
+
156
+ pip3 install coverage
157
+
158
+ Once `coverage.py` is installed, you can run the tests using the `runtests` script:
159
+
160
+ .. code-block:: bash
161
+
162
+ ./runtests
163
+
164
+ This script will run all the tests in the `tests` directory and generate a coverage report.
165
+
147
166
  ------------
148
167
  Getting Help
149
168
  ------------
@@ -224,3 +243,5 @@ This is not an officially supported Google product.
224
243
 
225
244
  .. _`Apigee CI/CD Docker releases`: https://hub.docker.com/r/darumatic/apigee-cicd
226
245
 
246
+
247
+
@@ -91,4 +91,7 @@ apigeecli.egg-info/dependency_links.txt
91
91
  apigeecli.egg-info/entry_points.txt
92
92
  apigeecli.egg-info/requires.txt
93
93
  apigeecli.egg-info/top_level.txt
94
- tests/__init__.py
94
+ tests/__init__.py
95
+ tests/test_auth.py
96
+ tests/test_echo.py
97
+ tests/test_encryption_utils.py
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  apigee = apigee.__main__:main
3
+
@@ -1,11 +1,11 @@
1
- click==8.1.3
1
+ GitPython==3.1.30
2
+ PyJWT==2.6.0
2
3
  click-aliases==1.0.1
3
4
  click-option-group==0.5.5
5
+ click==8.1.3
4
6
  colorama==0.4.6
5
7
  coverage==7.0.1
6
- GitPython==3.1.30
7
8
  pudb==2022.1.3
8
- PyJWT==2.6.0
9
9
  pyotp==2.8.0
10
10
  python-gnupg<0.5.0,>=0.3.5
11
11
  requests==2.28.1
File without changes
File without changes
File without changes
@@ -0,0 +1,63 @@
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+
4
+ from apigee.auth import get_access_token_for_sso
5
+
6
+ class TestAuth(unittest.TestCase):
7
+ def setUp(self):
8
+ self.auth = MagicMock()
9
+ self.username = "test_user"
10
+ self.password = "test_password"
11
+ self.oauth_url = "https://example.com/oauth"
12
+ self.post_headers = {"Content-Type": "application/x-www-form-urlencoded"}
13
+ self.session = MagicMock()
14
+
15
+ @patch("apigee.auth.validate_refresh_token")
16
+ @patch("apigee.auth.get_sso_temporary_authentication_code")
17
+ @patch("builtins.open")
18
+ def test_get_access_token_for_sso_no_refresh_token(self, mock_open, mock_get_sso_temporary_authentication_code, mock_validate_refresh_token):
19
+ mock_validate_refresh_token.return_value = None
20
+ mock_get_sso_temporary_authentication_code.return_value = "test_passcode"
21
+
22
+ response_data = {
23
+ "access_token": "test_access_token",
24
+ "refresh_token": "test_refresh_token"
25
+ }
26
+ self.session.post.return_value.json.return_value = response_data
27
+
28
+ result = get_access_token_for_sso(
29
+ self.auth, self.username, self.password, self.oauth_url, self.post_headers, self.session, None
30
+ )
31
+
32
+ mock_get_sso_temporary_authentication_code.assert_called_once_with(f"https://{self.auth.zonename}.login.apigee.com/passcode")
33
+ self.session.post.assert_called_once_with(
34
+ f"https://{self.auth.zonename}.login.apigee.com/oauth/token", headers=self.post_headers, data="passcode=test_passcode&grant_type=password&response_type=token"
35
+ )
36
+ self.assertEqual(result, response_data)
37
+ # mock_open.assert_not_called()
38
+ mock_open.assert_called_once_with('/home/mdelotavo/.apigee/refresh_token', 'w')
39
+
40
+ @patch("apigee.auth.validate_refresh_token")
41
+ @patch("builtins.open")
42
+ def test_get_access_token_for_sso_with_refresh_token(self, mock_open, mock_validate_refresh_token):
43
+ mock_validate_refresh_token.return_value = "test_refresh_token"
44
+
45
+ response_data = {
46
+ "access_token": "test_access_token",
47
+ "refresh_token": "test_refresh_token"
48
+ }
49
+ self.session.post.return_value.json.return_value = response_data
50
+
51
+ result = get_access_token_for_sso(
52
+ self.auth, self.username, self.password, self.oauth_url, self.post_headers, self.session, None
53
+ )
54
+
55
+ self.session.post.assert_called_once_with(
56
+ f"https://{self.auth.zonename}.login.apigee.com/oauth/token", headers=self.post_headers, data="grant_type=refresh_token&refresh_token=test_refresh_token"
57
+ )
58
+ self.assertEqual(result, response_data)
59
+ # mock_open.assert_called_once_with('/root/.apigee/refresh_token', 'w')
60
+ mock_open.assert_not_called()
61
+
62
+ if __name__ == '__main__':
63
+ unittest.main()
@@ -0,0 +1,35 @@
1
+ import unittest
2
+ from unittest.mock import patch
3
+ from apigee.console import echo
4
+
5
+ class TestEcho(unittest.TestCase):
6
+ @patch('builtins.print')
7
+ @patch('sys.exit')
8
+ def test_echo_silent(self, mock_exit, mock_print):
9
+ echo('Hello', make_silent=True, exit_status=1)
10
+ mock_print.assert_not_called()
11
+ mock_exit.assert_called_once_with(1)
12
+
13
+ @patch('builtins.print')
14
+ @patch('sys.exit')
15
+ def test_echo_not_silent(self, mock_exit, mock_print):
16
+ echo('Hello', make_silent=False)
17
+ mock_print.assert_called_once_with('Hello', end='\n', flush=False)
18
+ mock_exit.assert_not_called()
19
+
20
+ @patch('builtins.print')
21
+ @patch('sys.exit')
22
+ def test_echo_verbosity(self, mock_exit, mock_print):
23
+ echo('Hello', current_verbosity=1, expected_verbosity=0)
24
+ mock_print.assert_called_once_with('Hello', end='\n', flush=False)
25
+ mock_exit.assert_not_called()
26
+
27
+ @patch('builtins.print')
28
+ @patch('sys.exit')
29
+ def test_echo_no_verbosity(self, mock_exit, mock_print):
30
+ echo('Hello', current_verbosity=0, expected_verbosity=1)
31
+ mock_print.assert_not_called()
32
+ mock_exit.assert_not_called()
33
+
34
+ if __name__ == '__main__':
35
+ unittest.main()
@@ -0,0 +1,32 @@
1
+ import unittest
2
+
3
+ from apigee.encryption_utils import (ENCRYPTED_HEADER_BEGIN, ENCRYPTED_HEADER_END,
4
+ decrypt_message_with_gpg, encrypt_message_with_gpg)
5
+
6
+ class TestEncryptionUtils(unittest.TestCase):
7
+ ciphertext = None
8
+
9
+ @classmethod
10
+ def setUpClass(cls):
11
+ cls.secret = "password123!"
12
+ cls.plaintext = "Hello, World!"
13
+ cls.ciphertext = f"{ENCRYPTED_HEADER_BEGIN}{encrypt_message_with_gpg(cls.secret, cls.plaintext)}{ENCRYPTED_HEADER_END}"
14
+
15
+ def test_decrypt_message_with_gpg_encoded(self):
16
+ result = decrypt_message_with_gpg(self.secret, self.ciphertext, encoded=True)
17
+ self.assertEqual(result, self.plaintext)
18
+
19
+ def test_decrypt_message_with_gpg_encoded_plaintext_none(self):
20
+ result = decrypt_message_with_gpg(self.secret, None, encoded=True)
21
+ self.assertEqual(result, "")
22
+
23
+ def test_decrypt_message_with_gpg_encoded_plaintext_empty(self):
24
+ result = decrypt_message_with_gpg(self.secret, "", encoded=True)
25
+ self.assertEqual(result, "")
26
+
27
+ def test_decrypt_message_with_gpg_no_encrypted_header(self):
28
+ result = decrypt_message_with_gpg(self.secret, self.plaintext)
29
+ self.assertEqual(result, "")
30
+
31
+ if __name__ == '__main__':
32
+ unittest.main()