besapi 3.9.1__tar.gz → 3.9.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 (25) hide show
  1. {besapi-3.9.1/src/besapi.egg-info → besapi-3.9.4}/PKG-INFO +1 -1
  2. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/besapi.py +15 -1
  3. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/plugin_utilities.py +74 -12
  4. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/plugin_utilities_win.py +56 -7
  5. {besapi-3.9.1 → besapi-3.9.4/src/besapi.egg-info}/PKG-INFO +1 -1
  6. {besapi-3.9.1 → besapi-3.9.4}/tests/test_besapi.py +55 -14
  7. {besapi-3.9.1 → besapi-3.9.4}/LICENSE.txt +0 -0
  8. {besapi-3.9.1 → besapi-3.9.4}/MANIFEST.in +0 -0
  9. {besapi-3.9.1 → besapi-3.9.4}/README.md +0 -0
  10. {besapi-3.9.1 → besapi-3.9.4}/pyproject.toml +0 -0
  11. {besapi-3.9.1 → besapi-3.9.4}/setup.cfg +0 -0
  12. {besapi-3.9.1 → besapi-3.9.4}/setup.py +0 -0
  13. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/__init__.py +0 -0
  14. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/__main__.py +0 -0
  15. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BES.xsd +0 -0
  16. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BESAPI.xsd +0 -0
  17. {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BESActionSettings.xsd +0 -0
  18. {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/SOURCES.txt +0 -0
  19. {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/dependency_links.txt +0 -0
  20. {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/requires.txt +0 -0
  21. {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/top_level.txt +0 -0
  22. {besapi-3.9.1 → besapi-3.9.4}/src/bescli/__init__.py +0 -0
  23. {besapi-3.9.1 → besapi-3.9.4}/src/bescli/__main__.py +0 -0
  24. {besapi-3.9.1 → besapi-3.9.4}/src/bescli/bescli.py +0 -0
  25. {besapi-3.9.1 → besapi-3.9.4}/tests/tests.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: besapi
3
- Version: 3.9.1
3
+ Version: 3.9.4
4
4
  Summary: Library for working with the BigFix REST API
5
5
  Home-page: https://github.com/jgstew/besapi
6
6
  Author: Matt Hansen, James Stewart
@@ -25,7 +25,7 @@ import lxml.etree
25
25
  import lxml.objectify
26
26
  import requests
27
27
 
28
- __version__ = "3.9.1"
28
+ __version__ = "3.9.4"
29
29
 
30
30
  besapi_logger = logging.getLogger("besapi")
31
31
 
@@ -283,6 +283,20 @@ def validate_xml_bes_file(file_path):
283
283
  return validate_xsd(file_data)
284
284
 
285
285
 
286
+ def get_bes_conn_using_env():
287
+ """Get BESConnection using environment variables."""
288
+ username = os.getenv("BES_USER_NAME")
289
+ password = os.getenv("BES_PASSWORD")
290
+ rootserver = os.getenv("BES_ROOT_SERVER")
291
+
292
+ if username and password and rootserver:
293
+ bes_conn = BESConnection(username, password, rootserver)
294
+ if bes_conn:
295
+ return bes_conn
296
+
297
+ return None
298
+
299
+
286
300
  def get_bes_conn_using_config_file(conf_file=None):
287
301
  """
288
302
  Read connection values from config file.
@@ -10,6 +10,7 @@ import logging.handlers
10
10
  import ntpath
11
11
  import os
12
12
  import sys
13
+ from typing import Union
13
14
 
14
15
  import besapi
15
16
 
@@ -131,12 +132,35 @@ def get_plugin_logging_config(log_file_path="", verbose=0, console=True):
131
132
  }
132
133
 
133
134
 
134
- def get_besapi_connection(args):
135
- """Get connection to besapi using either args or config file if args not
136
- provided.
137
- """
135
+ def get_besapi_connection_env_then_config():
136
+ """Get connection to besapi using env vars first, then config file."""
137
+ logging.info("attempting connection to BigFix using ENV method.")
138
+ # try to get connection from env vars:
139
+ bes_conn = besapi.besapi.get_bes_conn_using_env()
140
+ if bes_conn:
141
+ return bes_conn
142
+
143
+ logging.info("attempting connection to BigFix using config file method.")
144
+ bes_conn = besapi.besapi.get_bes_conn_using_config_file()
145
+ return bes_conn
146
+
147
+
148
+ def get_besapi_connection_args(
149
+ args: argparse.Namespace,
150
+ ) -> Union[besapi.besapi.BESConnection, None]:
151
+ """Get connection to besapi using provided args."""
152
+ password = None
153
+ bes_conn = None
154
+
155
+ if args.password:
156
+ password = args.password
138
157
 
139
- password = args.password
158
+ # if user was provided as arg but password was not:
159
+ if args.user and not password:
160
+ if os.name == "nt":
161
+ # attempt to get password from windows root server registry:
162
+ # this is specifically for the case where user is provided for a plugin
163
+ password = besapi.plugin_utilities_win.get_win_registry_rest_pass()
140
164
 
141
165
  # if user was provided as arg but password was not:
142
166
  if args.user and not password:
@@ -144,7 +168,7 @@ def get_besapi_connection(args):
144
168
  print("Password was not provided, provide REST API password:")
145
169
  password = getpass.getpass()
146
170
 
147
- if args.user:
171
+ if password:
148
172
  logging.debug("REST API Password Length: %s", len(password))
149
173
 
150
174
  # process args, setup connection:
@@ -158,13 +182,13 @@ def get_besapi_connection(args):
158
182
  if args.user and password:
159
183
  try:
160
184
  if not rest_url:
161
- raise AttributeError
185
+ raise AttributeError("args.rest_url is not set.")
162
186
  bes_conn = besapi.besapi.BESConnection(args.user, password, rest_url)
163
187
  except (
164
188
  AttributeError,
165
189
  ConnectionRefusedError,
166
190
  besapi.besapi.requests.exceptions.ConnectionError,
167
- ):
191
+ ) as e:
168
192
  logging.exception(
169
193
  "connection to `%s` failed, attempting `%s` instead",
170
194
  rest_url,
@@ -172,7 +196,7 @@ def get_besapi_connection(args):
172
196
  )
173
197
  try:
174
198
  if not args.besserver:
175
- raise AttributeError
199
+ raise AttributeError("args.besserver is not set.") from e
176
200
  bes_conn = besapi.besapi.BESConnection(
177
201
  args.user, password, args.besserver
178
202
  )
@@ -185,7 +209,7 @@ def get_besapi_connection(args):
185
209
  )
186
210
  return None
187
211
  except BaseException as err:
188
- # always log error and stop the current process
212
+ # always log error
189
213
  logging.exception("ERROR: %s", err)
190
214
  logging.exception(
191
215
  "----- ERROR: BigFix Connection Failed! Unknown reason ------"
@@ -193,8 +217,46 @@ def get_besapi_connection(args):
193
217
  return None
194
218
  else:
195
219
  logging.info(
196
- "attempting connection to BigFix using config file method as user command arg was not provided"
220
+ "No user arg provided, no password found. Cannot create connection."
221
+ )
222
+ return None
223
+
224
+ return bes_conn
225
+
226
+
227
+ def get_besapi_connection(
228
+ args: Union[argparse.Namespace, None] = None,
229
+ ) -> Union[besapi.besapi.BESConnection, None]:
230
+ """Get connection to besapi.
231
+
232
+ If on Windows, will attempt to get connection from Windows Registry first.
233
+ If args provided, will attempt to get connection using provided args.
234
+ If no args provided, will attempt to get connection from env vars.
235
+ If no env vars, will attempt to get connection from config file.
236
+
237
+ Arguments:
238
+ args: argparse.Namespace object, usually from setup_plugin_argparse()
239
+ Returns:
240
+ A BESConnection object if successful, otherwise None.
241
+ """
242
+ # if windows, try to get connection from windows registry:
243
+ if os.name == "nt":
244
+ bes_conn = besapi.plugin_utilities_win.get_besconn_root_windows_registry()
245
+ if bes_conn:
246
+ return bes_conn
247
+
248
+ # if no args provided, try to get connection from env then config file:
249
+ if not args:
250
+ logging.info("no args provided, attempting connection using env then config.")
251
+ return get_besapi_connection_env_then_config()
252
+
253
+ # attempt bigfix connection with provided args:
254
+ if args.user:
255
+ bes_conn = get_besapi_connection_args(args)
256
+ else:
257
+ logging.info(
258
+ "no user arg provided, attempting connection using env then config."
197
259
  )
198
- bes_conn = besapi.besapi.get_bes_conn_using_config_file()
260
+ return get_besapi_connection_env_then_config()
199
261
 
200
262
  return bes_conn
@@ -8,6 +8,8 @@ import logging
8
8
  import sys
9
9
  from typing import Union
10
10
 
11
+ import besapi
12
+
11
13
  logger = logging.getLogger(__name__)
12
14
 
13
15
  try:
@@ -105,7 +107,12 @@ def win_dpapi_decrypt_base64(
105
107
  scope_flags,
106
108
  )
107
109
 
108
- return decrypted_bytes.decode("utf-8") if decrypted_bytes else None
110
+ if decrypted_bytes:
111
+ decrypted_string = decrypted_bytes.decode("utf-8").strip()
112
+ return decrypted_string
113
+
114
+ logger.debug("Decryption returned no data.")
115
+ return None
109
116
 
110
117
 
111
118
  def win_registry_value_read(hive, subkey, value_name):
@@ -132,21 +139,21 @@ def win_registry_value_read(hive, subkey, value_name):
132
139
 
133
140
  return value_data
134
141
  except FileNotFoundError:
135
- logger.error("Registry key or value '%s\\%s' not found.", subkey, value_name)
142
+ logger.debug("Registry key or value '%s\\%s' not found.", subkey, value_name)
136
143
  return None
137
144
  except Exception as e:
138
145
  logger.error("An error occurred: %s", e)
139
146
  return None
140
147
 
141
148
 
142
- def get_win_registry_rest_pass():
149
+ def get_win_registry_rest_pass() -> Union[str, None]:
143
150
  """
144
151
  Retrieves the base64 encrypted REST Password from the Windows Registry.
145
152
 
146
153
  Returns:
147
154
  The REST Password if found, otherwise None.
148
155
  """
149
- hive = winreg.HKEY_LOCAL_MACHINE
156
+ hive = winreg.HKEY_LOCAL_MACHINE # type: ignore[attr-defined]
150
157
  # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig
151
158
  subkey = r"SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig"
152
159
  value_name = "RESTPassword"
@@ -154,14 +161,56 @@ def get_win_registry_rest_pass():
154
161
  reg_value = win_registry_value_read(hive, subkey, value_name)
155
162
 
156
163
  if not reg_value:
157
- logger.error("No registry value found for RESTPassword.")
164
+ logger.debug("No registry value found for %s.", value_name)
158
165
  return None
159
166
 
160
167
  # remove {obf} from start of string if present:
161
168
  if reg_value and reg_value.startswith("{obf}"):
162
169
  reg_value = reg_value[5:]
163
170
 
164
- if reg_value:
165
- return win_dpapi_decrypt_base64(reg_value)
171
+ if reg_value and len(reg_value) > 50:
172
+ password = win_dpapi_decrypt_base64(reg_value)
173
+ if password and len(password) > 3:
174
+ return password
175
+
176
+ logger.debug("Decryption failed or decrypted password length is too short.")
177
+ return None
178
+
179
+
180
+ def get_besconn_root_windows_registry() -> Union[besapi.besapi.BESConnection, None]:
181
+ """
182
+ Attempts to create a BESConnection using credentials stored in the Windows
183
+ Registry.
184
+
185
+ Returns:
186
+ A BESConnection object if successful, otherwise None.
187
+ """
188
+ password = get_win_registry_rest_pass()
189
+ if not password:
190
+ return None
191
+
192
+ hive = winreg.HKEY_LOCAL_MACHINE # type: ignore[attr-defined]
193
+ subkey = r"SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig"
194
+
195
+ user = win_registry_value_read(hive, subkey, "RESTUsername")
196
+
197
+ if not user:
198
+ return None
199
+
200
+ rest_url = win_registry_value_read(hive, subkey, "RESTURL")
201
+
202
+ if not rest_url:
203
+ return None
204
+
205
+ # normalize url to https://HostOrIP:52311
206
+ if rest_url and rest_url.endswith("/api"):
207
+ rest_url = rest_url.replace("/api", "")
208
+
209
+ if user and password and rest_url:
210
+ try:
211
+ return besapi.besapi.BESConnection(user, password, rest_url)
212
+ except Exception as e:
213
+ logger.error("Failed to create BESConnection from registry values: %s", e)
214
+ return None
166
215
 
167
216
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: besapi
3
- Version: 3.9.1
3
+ Version: 3.9.4
4
4
  Summary: Library for working with the BigFix REST API
5
5
  Home-page: https://github.com/jgstew/besapi
6
6
  Author: Matt Hansen, James Stewart
@@ -231,48 +231,89 @@ def test_bescli():
231
231
  assert bes_conn.login()
232
232
 
233
233
 
234
+ def test_plugin_utilities_win_get_besconn_root_windows_registry():
235
+ """Test getting a BESConnection from the Windows Registry."""
236
+ if "BES_ROOT_SERVER" not in os.environ:
237
+ pytest.skip("Skipping Windows Registry test, BES_ROOT_SERVER not set.")
238
+ if "BES_USER_NAME" not in os.environ:
239
+ pytest.skip("Skipping Windows Registry test, BES_USER_NAME not set.")
240
+ if "BES_PASSWORD" not in os.environ:
241
+ pytest.skip("Skipping Windows Registry test, BES_PASSWORD not set.")
242
+
243
+ if not os.name == "nt":
244
+ pytest.skip("Skipping Windows Registry test on non-Windows system.")
245
+
246
+ # only run this test if besapi > v3.9.1:
247
+ if besapi.besapi.__version__ <= "3.9.1":
248
+ pytest.skip("Skipping test for besapi <= 3.9.1")
249
+
250
+ # get env vars for testing:
251
+ root_server = os.getenv("BES_ROOT_SERVER")
252
+ root_user = os.getenv("BES_USER_NAME")
253
+ root_user_password = os.getenv("BES_PASSWORD")
254
+
255
+ encrypted_str = besapi.plugin_utilities_win.win_dpapi_encrypt_str(
256
+ root_user_password
257
+ )
258
+
259
+ import winreg
260
+
261
+ # write user and encrypted password to registry for testing:
262
+ # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig
263
+ subkey = r"SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig"
264
+ hive = winreg.HKEY_LOCAL_MACHINE # type: ignore[attr-defined]
265
+
266
+ key = winreg.CreateKey(hive, subkey)
267
+ winreg.SetValueEx(
268
+ key, "RESTURL", 0, winreg.REG_SZ, "https://" + root_server + ":52311/api"
269
+ )
270
+ winreg.SetValueEx(key, "RESTUsername", 0, winreg.REG_SZ, root_user)
271
+ winreg.SetValueEx(key, "RESTPassword", 0, winreg.REG_SZ, "{obf}" + encrypted_str)
272
+ winreg.CloseKey(key)
273
+ bes_conn = besapi.plugin_utilities_win.get_besconn_root_windows_registry()
274
+ assert bes_conn is not None
275
+
276
+
234
277
  def test_bes_conn_json():
235
278
  """Test the BESConnection class with JSON output."""
236
- bes_conn = besapi.besapi.get_bes_conn_using_config_file()
279
+
280
+ bes_conn = besapi.plugin_utilities.get_besapi_connection(None)
281
+
237
282
  if bes_conn and bes_conn.login():
238
283
  print("testing session_relevance_json")
239
- result = bes_conn.session_relevance_json("number of bes computers")
284
+ result = bes_conn.session_relevance_json("number of all bes sites")
240
285
  assert result is not None
241
286
  assert int(result["result"][0]) > 0
242
287
  result = bes_conn.session_relevance_json(
243
- """("[%22" & it & "%22]") of concatenation "%22, %22" of names of bes computers"""
288
+ """("[%22" & it & "%22]") of concatenation "%22, %22" of names of all bes sites"""
244
289
  )
245
290
  assert result is not None
246
291
  string_first_result_json = result["result"][0]
247
292
  print(string_first_result_json)
248
293
  assert '", "' in string_first_result_json
249
294
  assert '["' in string_first_result_json
250
- # NOTE: the following check is specific to my env:
251
- if "10.0." in bes_conn.rootserver:
252
- print("doing check specific to my env")
253
- assert '", "BIGFIX", "' in string_first_result_json
295
+ assert '"BES Support"' in string_first_result_json
254
296
 
255
297
  print("testing session_relevance_json_array")
256
- result = bes_conn.session_relevance_json_array("number of bes computers")
298
+ result = bes_conn.session_relevance_json_array("number of all bes sites")
257
299
  print(result)
258
300
  assert result is not None
259
301
  assert int(result[0]) > 0
260
302
  print("testing session_relevance_json_string")
261
- result = bes_conn.session_relevance_json_string("number of bes computers")
303
+ result = bes_conn.session_relevance_json_string("number of all bes sites")
262
304
  print(result)
263
305
  assert result is not None
264
306
  assert int(result) > 0
265
307
  print("testing session_relevance_json_string tuple")
266
308
  result = bes_conn.session_relevance_json_string(
267
- '(ids of it, names of it, "TestString") of bes computers'
309
+ '(ids of it, names of it, "TestString") of all bes sites'
268
310
  )
269
311
  print(result)
270
312
  assert result is not None
271
313
  assert "TestString" in result
272
- # NOTE: the following check is specific to my env:
273
- if "10.0." in bes_conn.rootserver:
274
- print("doing check specific to my env")
275
- assert "BIGFIX" in result
314
+ assert "BES Support" in result
315
+ else:
316
+ pytest.skip("Skipping BESConnection test, no config file or login failed.")
276
317
 
277
318
 
278
319
  def test_plugin_utilities_logging():
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes