besapi 3.8.3__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.8.3/src/besapi.egg-info → besapi-3.9.4}/PKG-INFO +1 -1
  2. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/besapi.py +17 -1
  3. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/plugin_utilities.py +74 -12
  4. besapi-3.9.4/src/besapi/plugin_utilities_win.py +216 -0
  5. {besapi-3.8.3 → besapi-3.9.4/src/besapi.egg-info}/PKG-INFO +1 -1
  6. {besapi-3.8.3 → besapi-3.9.4}/src/besapi.egg-info/SOURCES.txt +1 -0
  7. {besapi-3.8.3 → besapi-3.9.4}/tests/test_besapi.py +134 -10
  8. {besapi-3.8.3 → besapi-3.9.4}/LICENSE.txt +0 -0
  9. {besapi-3.8.3 → besapi-3.9.4}/MANIFEST.in +0 -0
  10. {besapi-3.8.3 → besapi-3.9.4}/README.md +0 -0
  11. {besapi-3.8.3 → besapi-3.9.4}/pyproject.toml +0 -0
  12. {besapi-3.8.3 → besapi-3.9.4}/setup.cfg +0 -0
  13. {besapi-3.8.3 → besapi-3.9.4}/setup.py +0 -0
  14. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/__init__.py +0 -0
  15. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/__main__.py +0 -0
  16. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/schemas/BES.xsd +0 -0
  17. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/schemas/BESAPI.xsd +0 -0
  18. {besapi-3.8.3 → besapi-3.9.4}/src/besapi/schemas/BESActionSettings.xsd +0 -0
  19. {besapi-3.8.3 → besapi-3.9.4}/src/besapi.egg-info/dependency_links.txt +0 -0
  20. {besapi-3.8.3 → besapi-3.9.4}/src/besapi.egg-info/requires.txt +0 -0
  21. {besapi-3.8.3 → besapi-3.9.4}/src/besapi.egg-info/top_level.txt +0 -0
  22. {besapi-3.8.3 → besapi-3.9.4}/src/bescli/__init__.py +0 -0
  23. {besapi-3.8.3 → besapi-3.9.4}/src/bescli/__main__.py +0 -0
  24. {besapi-3.8.3 → besapi-3.9.4}/src/bescli/bescli.py +0 -0
  25. {besapi-3.8.3 → 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.8.3
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.8.3"
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.
@@ -479,6 +493,8 @@ class BESConnection:
479
493
 
480
494
  This will submit the relevance string as json instead of html form data.
481
495
  """
496
+ # not sure if the following is needed to handle some cases:
497
+ # relevance = "(it as string) of ( " + relevance + " )"
482
498
  rel_result_array = self.session_relevance_json_array(relevance, **kwargs)
483
499
  # Ensure each element is converted to a string
484
500
  return "\n".join(map(str, rel_result_array))
@@ -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
@@ -0,0 +1,216 @@
1
+ """
2
+ Plugin utilities for Windows systems, including DPAPI encryption/decryption
3
+ and Windows Registry access.
4
+ """
5
+
6
+ import base64
7
+ import logging
8
+ import sys
9
+ from typing import Union
10
+
11
+ import besapi
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ try:
16
+ import winreg
17
+ except (ImportError, ModuleNotFoundError) as e:
18
+ if not sys.platform.startswith("win"):
19
+ raise RuntimeError(
20
+ "This script requires the 'winreg' module, which is only available on Windows."
21
+ ) from e
22
+ else:
23
+ raise e
24
+
25
+ try:
26
+ import win32crypt
27
+ except (ImportError, ModuleNotFoundError) as e:
28
+ if not sys.platform.startswith("win"):
29
+ raise RuntimeError("This script only works on Windows systems") from e
30
+ raise ImportError(
31
+ "This script requires the pywin32 package. Install it via 'pip install pywin32'."
32
+ ) from e
33
+
34
+
35
+ def win_dpapi_encrypt_str(
36
+ plaintext: str, scope_flags: int = 4, entropy: Union[str, bytes, None] = None
37
+ ) -> Union[str, None]:
38
+ """Encrypt a string using Windows DPAPI and return it as a base64-encoded string.
39
+
40
+ Args:
41
+ plaintext (str): The string to encrypt.
42
+ scope_flags (int): The context scope for encryption (default is 4).
43
+ entropy (bytes | None): Optional entropy for encryption.
44
+
45
+ Returns:
46
+ str: The base64-encoded encrypted string.
47
+ """
48
+ if not plaintext or plaintext.strip() == "":
49
+ logger.warning("No plaintext provided for encryption.")
50
+ return None
51
+
52
+ # 1. Convert the plaintext string to bytes
53
+ plaintext_bytes = plaintext.encode("utf-8")
54
+
55
+ # 2. Call CryptProtectData.
56
+ # The last parameter (flags) is set to 4
57
+ # to indicate that the data should be encrypted in the machine context.
58
+ #
59
+ # The function returns a tuple: (description, encrypted_bytes)
60
+ # We only need the second element.
61
+ encrypted_bytes = win32crypt.CryptProtectData(
62
+ plaintext_bytes,
63
+ None, # Description
64
+ entropy, # Optional entropy
65
+ None, # Reserved
66
+ None, # Prompt Struct
67
+ scope_flags,
68
+ )
69
+
70
+ # 3. Encode the encrypted bytes to a Base64 string
71
+ encrypted_b64 = base64.b64encode(encrypted_bytes).decode("utf-8")
72
+
73
+ return encrypted_b64
74
+
75
+
76
+ def win_dpapi_decrypt_base64(
77
+ encrypted_b64: str, scope_flags: int = 4, entropy: Union[str, bytes, None] = None
78
+ ) -> Union[str, None]:
79
+ """Decrypt a base64-encoded string encrypted with Windows DPAPI.
80
+
81
+ Args:
82
+ encrypted_b64 (str): The base64-encoded encrypted string.
83
+ scope_flags (int): The context scope for decryption (default is 4).
84
+ entropy (bytes | None): Optional entropy for decryption.
85
+
86
+ Returns:
87
+ str: The decrypted string.
88
+ """
89
+ if not encrypted_b64 or encrypted_b64.strip() == "":
90
+ logger.warning("No encrypted data provided for decryption.")
91
+ return None
92
+
93
+ # 1. Decode the Base64 string to get the raw encrypted bytes
94
+ encrypted_bytes = base64.b64decode(encrypted_b64)
95
+
96
+ # 2. Call CryptUnprotectData.
97
+ # The last parameter (flags) is set to 4
98
+ # to indicate that the data was encrypted in the machine context.
99
+ #
100
+ # The function returns a tuple: (description, decrypted_bytes)
101
+ # We only need the second element.
102
+ _, decrypted_bytes = win32crypt.CryptUnprotectData(
103
+ encrypted_bytes,
104
+ entropy, # Optional entropy
105
+ None, # Reserved
106
+ None, # Prompt Struct
107
+ scope_flags,
108
+ )
109
+
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
116
+
117
+
118
+ def win_registry_value_read(hive, subkey, value_name):
119
+ """
120
+ Reads a value from the Windows Registry.
121
+
122
+ Args:
123
+ hive: The root hive (e.g., winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER).
124
+ subkey: The path to the subkey (e.g., "SOFTWARE\\Microsoft\\Windows\\CurrentVersion").
125
+ value_name: The name of the value to read.
126
+
127
+ Returns:
128
+ The value data if found, otherwise None.
129
+ """
130
+ try:
131
+ # Open the specified registry key
132
+ key = winreg.OpenKey(hive, subkey, 0, winreg.KEY_READ)
133
+
134
+ # Query the value data
135
+ value_data, _ = winreg.QueryValueEx(key, value_name)
136
+
137
+ # Close the key
138
+ winreg.CloseKey(key)
139
+
140
+ return value_data
141
+ except FileNotFoundError:
142
+ logger.debug("Registry key or value '%s\\%s' not found.", subkey, value_name)
143
+ return None
144
+ except Exception as e:
145
+ logger.error("An error occurred: %s", e)
146
+ return None
147
+
148
+
149
+ def get_win_registry_rest_pass() -> Union[str, None]:
150
+ """
151
+ Retrieves the base64 encrypted REST Password from the Windows Registry.
152
+
153
+ Returns:
154
+ The REST Password if found, otherwise None.
155
+ """
156
+ hive = winreg.HKEY_LOCAL_MACHINE # type: ignore[attr-defined]
157
+ # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig
158
+ subkey = r"SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig"
159
+ value_name = "RESTPassword"
160
+
161
+ reg_value = win_registry_value_read(hive, subkey, value_name)
162
+
163
+ if not reg_value:
164
+ logger.debug("No registry value found for %s.", value_name)
165
+ return None
166
+
167
+ # remove {obf} from start of string if present:
168
+ if reg_value and reg_value.startswith("{obf}"):
169
+ reg_value = reg_value[5:]
170
+
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
215
+
216
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: besapi
3
- Version: 3.8.3
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
@@ -8,6 +8,7 @@ src/besapi/__init__.py
8
8
  src/besapi/__main__.py
9
9
  src/besapi/besapi.py
10
10
  src/besapi/plugin_utilities.py
11
+ src/besapi/plugin_utilities_win.py
11
12
  src/besapi.egg-info/PKG-INFO
12
13
  src/besapi.egg-info/SOURCES.txt
13
14
  src/besapi.egg-info/dependency_links.txt
@@ -25,6 +25,9 @@ if not os.getenv("TEST_PIP"):
25
25
  import besapi
26
26
  import besapi.plugin_utilities
27
27
 
28
+ if os.name == "nt":
29
+ import besapi.plugin_utilities_win
30
+
28
31
 
29
32
  def test_besapi_version():
30
33
  """Test that the besapi version is not None."""
@@ -228,40 +231,92 @@ def test_bescli():
228
231
  assert bes_conn.login()
229
232
 
230
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
+
231
277
  def test_bes_conn_json():
232
278
  """Test the BESConnection class with JSON output."""
233
- bes_conn = besapi.besapi.get_bes_conn_using_config_file()
279
+
280
+ bes_conn = besapi.plugin_utilities.get_besapi_connection(None)
281
+
234
282
  if bes_conn and bes_conn.login():
235
283
  print("testing session_relevance_json")
236
- result = bes_conn.session_relevance_json("number of bes computers")
284
+ result = bes_conn.session_relevance_json("number of all bes sites")
237
285
  assert result is not None
238
286
  assert int(result["result"][0]) > 0
239
287
  result = bes_conn.session_relevance_json(
240
- """("[%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"""
241
289
  )
242
290
  assert result is not None
243
291
  string_first_result_json = result["result"][0]
244
292
  print(string_first_result_json)
245
293
  assert '", "' in string_first_result_json
246
294
  assert '["' in string_first_result_json
247
- # NOTE: the following check is specific to my env:
248
- if "10.0." in bes_conn.rootserver:
249
- print("doing check specific to my env")
250
- assert '", "BIGFIX", "' in string_first_result_json
295
+ assert '"BES Support"' in string_first_result_json
251
296
 
252
297
  print("testing session_relevance_json_array")
253
- result = bes_conn.session_relevance_json_array("number of bes computers")
298
+ result = bes_conn.session_relevance_json_array("number of all bes sites")
254
299
  print(result)
255
300
  assert result is not None
256
301
  assert int(result[0]) > 0
257
302
  print("testing session_relevance_json_string")
258
- result = bes_conn.session_relevance_json_string("number of bes computers")
303
+ result = bes_conn.session_relevance_json_string("number of all bes sites")
259
304
  print(result)
260
305
  assert result is not None
261
306
  assert int(result) > 0
307
+ print("testing session_relevance_json_string tuple")
308
+ result = bes_conn.session_relevance_json_string(
309
+ '(ids of it, names of it, "TestString") of all bes sites'
310
+ )
311
+ print(result)
312
+ assert result is not None
313
+ assert "TestString" in result
314
+ assert "BES Support" in result
315
+ else:
316
+ pytest.skip("Skipping BESConnection test, no config file or login failed.")
262
317
 
263
318
 
264
- def test_plugin_utilities():
319
+ def test_plugin_utilities_logging():
265
320
  """Test the plugin_utilities module."""
266
321
  print(besapi.plugin_utilities.get_invoke_folder())
267
322
  print(besapi.plugin_utilities.get_invoke_file_name())
@@ -282,3 +337,72 @@ def test_plugin_utilities():
282
337
  logging.warning("Just testing to see if logging is working!")
283
338
 
284
339
  assert os.path.isfile("./tests.log")
340
+
341
+
342
+ def test_plugin_utilities_win_dpapi():
343
+ """Test the Windows DPAPI encryption function, if on Windows."""
344
+ if not os.name == "nt":
345
+ pytest.skip("Skipping Windows Registry test on non-Windows system.")
346
+
347
+ # only run this test if besapi > v3.8.3:
348
+ if besapi.besapi.__version__ <= "3.8.3":
349
+ pytest.skip("Skipping test for besapi <= 3.8.3")
350
+
351
+ test_string = "This is just a test string " + str(random.randint(0, 9999))
352
+ encrypted_str = besapi.plugin_utilities_win.win_dpapi_encrypt_str(test_string)
353
+ print("Encrypted string:", encrypted_str)
354
+ assert encrypted_str != ""
355
+ assert encrypted_str != test_string
356
+ decrypted_str = besapi.plugin_utilities_win.win_dpapi_decrypt_base64(encrypted_str)
357
+ print("Decrypted string:", decrypted_str)
358
+ assert decrypted_str == test_string
359
+
360
+
361
+ def test_plugin_utilities_win_win_registry_value_read():
362
+ """Test reading a Windows registry value."""
363
+ if not os.name == "nt":
364
+ pytest.skip("Skipping Windows Registry test on non-Windows system.")
365
+
366
+ # only run this test if besapi > v3.8.3:
367
+ if besapi.besapi.__version__ <= "3.8.3":
368
+ pytest.skip("Skipping test for besapi <= 3.8.3")
369
+
370
+ import winreg
371
+
372
+ registry_key = r"SOFTWARE\Microsoft\Windows\CurrentVersion"
373
+ registry_value = "ProgramFilesDir"
374
+ result = besapi.plugin_utilities_win.win_registry_value_read(
375
+ winreg.HKEY_LOCAL_MACHINE, registry_key, registry_value
376
+ )
377
+
378
+ assert result is not None
379
+ print("Registry value:", result)
380
+ assert "Program Files" in result
381
+
382
+
383
+ def test_plugin_utilities_win_get_win_registry_rest_pass():
384
+ """Test getting the Windows Registry REST password."""
385
+ if not os.name == "nt":
386
+ pytest.skip("Skipping Windows Registry test on non-Windows system.")
387
+
388
+ # only run this test if besapi > v3.8.3:
389
+ if besapi.besapi.__version__ <= "3.8.3":
390
+ pytest.skip("Skipping test for besapi <= 3.8.3")
391
+
392
+ import winreg
393
+
394
+ test_string = "This is just a test string " + str(random.randint(0, 9999))
395
+ encrypted_str = besapi.plugin_utilities_win.win_dpapi_encrypt_str(test_string)
396
+
397
+ # write encrypted string to registry for testing:
398
+ # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig
399
+ subkey = r"SOFTWARE\Wow6432Node\BigFix\Enterprise Server\MFSConfig"
400
+
401
+ key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, subkey)
402
+ winreg.SetValueEx(key, "RESTPassword", 0, winreg.REG_SZ, "{obf}" + encrypted_str)
403
+ winreg.CloseKey(key)
404
+
405
+ result = besapi.plugin_utilities_win.get_win_registry_rest_pass()
406
+ assert result is not None
407
+ print("Windows Registry REST password:", result)
408
+ assert result == test_string
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