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.
- {besapi-3.9.1/src/besapi.egg-info → besapi-3.9.4}/PKG-INFO +1 -1
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/besapi.py +15 -1
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/plugin_utilities.py +74 -12
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/plugin_utilities_win.py +56 -7
- {besapi-3.9.1 → besapi-3.9.4/src/besapi.egg-info}/PKG-INFO +1 -1
- {besapi-3.9.1 → besapi-3.9.4}/tests/test_besapi.py +55 -14
- {besapi-3.9.1 → besapi-3.9.4}/LICENSE.txt +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/MANIFEST.in +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/README.md +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/pyproject.toml +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/setup.cfg +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/setup.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/__init__.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/__main__.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BES.xsd +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BESAPI.xsd +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi/schemas/BESActionSettings.xsd +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/SOURCES.txt +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/dependency_links.txt +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/requires.txt +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/besapi.egg-info/top_level.txt +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/bescli/__init__.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/bescli/__main__.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/src/bescli/bescli.py +0 -0
- {besapi-3.9.1 → besapi-3.9.4}/tests/tests.py +0 -0
|
@@ -25,7 +25,7 @@ import lxml.etree
|
|
|
25
25
|
import lxml.objectify
|
|
26
26
|
import requests
|
|
27
27
|
|
|
28
|
-
__version__ = "3.9.
|
|
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
|
|
135
|
-
"""Get connection to besapi using
|
|
136
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|