pyxecm 1.5__py3-none-any.whl → 2.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/__init__.py +6 -2
- pyxecm/avts.py +1492 -0
- pyxecm/coreshare.py +1075 -960
- pyxecm/customizer/__init__.py +16 -4
- pyxecm/customizer/__main__.py +58 -0
- pyxecm/customizer/api/__init__.py +5 -0
- pyxecm/customizer/api/__main__.py +6 -0
- pyxecm/customizer/api/app.py +914 -0
- pyxecm/customizer/api/auth.py +154 -0
- pyxecm/customizer/api/metrics.py +92 -0
- pyxecm/customizer/api/models.py +13 -0
- pyxecm/customizer/api/payload_list.py +865 -0
- pyxecm/customizer/api/settings.py +103 -0
- pyxecm/customizer/browser_automation.py +332 -139
- pyxecm/customizer/customizer.py +1075 -1057
- pyxecm/customizer/exceptions.py +35 -0
- pyxecm/customizer/guidewire.py +322 -0
- pyxecm/customizer/k8s.py +787 -338
- pyxecm/customizer/log.py +107 -0
- pyxecm/customizer/m365.py +3424 -2270
- pyxecm/customizer/nhc.py +1169 -0
- pyxecm/customizer/openapi.py +258 -0
- pyxecm/customizer/payload.py +18201 -7030
- pyxecm/customizer/pht.py +1047 -210
- pyxecm/customizer/salesforce.py +836 -727
- pyxecm/customizer/sap.py +58 -41
- pyxecm/customizer/servicenow.py +851 -383
- pyxecm/customizer/settings.py +442 -0
- pyxecm/customizer/successfactors.py +408 -346
- pyxecm/customizer/translate.py +83 -48
- pyxecm/helper/__init__.py +5 -2
- pyxecm/helper/assoc.py +98 -38
- pyxecm/helper/data.py +2482 -742
- pyxecm/helper/logadapter.py +27 -0
- pyxecm/helper/web.py +229 -101
- pyxecm/helper/xml.py +528 -172
- pyxecm/maintenance_page/__init__.py +5 -0
- pyxecm/maintenance_page/__main__.py +6 -0
- pyxecm/maintenance_page/app.py +51 -0
- pyxecm/maintenance_page/settings.py +28 -0
- pyxecm/maintenance_page/static/favicon.avif +0 -0
- pyxecm/maintenance_page/templates/maintenance.html +165 -0
- pyxecm/otac.py +234 -140
- pyxecm/otawp.py +2689 -0
- pyxecm/otcs.py +12344 -7547
- pyxecm/otds.py +3166 -2219
- pyxecm/otiv.py +36 -21
- pyxecm/otmm.py +1363 -296
- pyxecm/otpd.py +231 -127
- pyxecm-2.0.0.dist-info/METADATA +145 -0
- pyxecm-2.0.0.dist-info/RECORD +54 -0
- {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
- pyxecm-1.5.dist-info/METADATA +0 -51
- pyxecm-1.5.dist-info/RECORD +0 -30
- {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/top_level.txt +0 -0
pyxecm/otac.py
CHANGED
|
@@ -1,58 +1,57 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OTAC Module to implement functions to apply Archive Center settings
|
|
3
|
-
|
|
4
|
-
Class: OTAC
|
|
5
|
-
Methods:
|
|
6
|
-
|
|
7
|
-
__init__ : class initializer
|
|
8
|
-
config : returns config data set
|
|
9
|
-
hostname: returns the Archive Center hostname
|
|
10
|
-
set_hostname: sets the Archive Center hostname
|
|
11
|
-
credentials: Get credentials (username + password)
|
|
12
|
-
set_credentials: Set the credentials for Archive Center for the "ds" and "admin" users
|
|
13
|
-
base_url: Returns the Archive Center base URL
|
|
14
|
-
exec_url: Returns the Archive Center URL to execute commands
|
|
15
|
-
request_form_header: Deliver the FORM request header used for the SOAP calls.
|
|
16
|
-
request_json_header: Deliver the JSON request header used for the CRUD REST API calls.
|
|
17
|
-
parse_request_response: Converts the text property of a request response object to a
|
|
18
|
-
Python dict in a safe way that also handles exceptions.
|
|
19
|
-
authenticate: Authenticates at Archive Center and retrieve Ticket
|
|
20
|
-
authenticate_soap: Authenticate via SOAP with admin User
|
|
21
|
-
exec_command: exec a command on Archive Center
|
|
22
|
-
put_cert: put Certificate on Archive Center
|
|
23
|
-
enable_cert: enables Certitificate on Archive Center via SOAP
|
|
24
|
-
enable_certificate: Enable a certificate via the new REST API
|
|
25
|
-
(replacing the old SOAP interface)
|
|
26
|
-
"""
|
|
1
|
+
"""OTAC Module to implement functions to apply Archive Center settings."""
|
|
27
2
|
|
|
28
3
|
__author__ = "Dr. Marc Diefenbruch"
|
|
29
|
-
__copyright__ = "Copyright 2024, OpenText"
|
|
4
|
+
__copyright__ = "Copyright (C) 2024-2025, OpenText"
|
|
30
5
|
__credits__ = ["Kai-Philip Gatzweiler"]
|
|
31
6
|
__maintainer__ = "Dr. Marc Diefenbruch"
|
|
32
7
|
__email__ = "mdiefenb@opentext.com"
|
|
33
8
|
|
|
34
|
-
import logging
|
|
35
|
-
import os
|
|
36
9
|
import base64
|
|
37
10
|
import json
|
|
38
|
-
import
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import sys
|
|
15
|
+
from importlib.metadata import version
|
|
39
16
|
|
|
40
|
-
|
|
17
|
+
import requests
|
|
41
18
|
from suds import WebFault
|
|
19
|
+
from suds.client import Client
|
|
20
|
+
|
|
21
|
+
APP_NAME = "pyxecm"
|
|
22
|
+
APP_VERSION = version("pyxecm")
|
|
23
|
+
MODULE_NAME = APP_NAME + ".otac"
|
|
42
24
|
|
|
43
|
-
|
|
25
|
+
PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
26
|
+
OS_INFO = f"{platform.system()} {platform.release()}"
|
|
27
|
+
ARCH_INFO = platform.machine()
|
|
28
|
+
REQUESTS_VERSION = requests.__version__
|
|
44
29
|
|
|
45
|
-
|
|
30
|
+
USER_AGENT = (
|
|
31
|
+
f"{APP_NAME}/{APP_VERSION} ({MODULE_NAME}/{APP_VERSION}; "
|
|
32
|
+
f"Python/{PYTHON_VERSION}; {OS_INFO}; {ARCH_INFO}; Requests/{REQUESTS_VERSION})"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
REQUEST_FORM_HEADERS = {
|
|
36
|
+
"User-Agent": USER_AGENT,
|
|
37
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
38
|
+
}
|
|
46
39
|
|
|
47
40
|
REQUEST_JSON_HEADERS = {
|
|
41
|
+
"User-Agent": USER_AGENT,
|
|
48
42
|
"accept": "application/json;charset=utf-8",
|
|
49
43
|
"Content-Type": "application/json",
|
|
50
44
|
}
|
|
51
45
|
|
|
52
46
|
REQUEST_TIMEOUT = 60
|
|
53
47
|
|
|
48
|
+
default_logger = logging.getLogger(MODULE_NAME)
|
|
49
|
+
|
|
50
|
+
|
|
54
51
|
class OTAC:
|
|
55
|
-
"""
|
|
52
|
+
"""Class OTAC is used to automate stettings in OpenText Archive Center."""
|
|
53
|
+
|
|
54
|
+
logger: logging.Logger = default_logger
|
|
56
55
|
|
|
57
56
|
_config = None
|
|
58
57
|
_soap_token: str = ""
|
|
@@ -68,19 +67,37 @@ class OTAC:
|
|
|
68
67
|
admin_username: str,
|
|
69
68
|
admin_password: str,
|
|
70
69
|
otds_ticket: str | None = None,
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
logger: logging.Logger = default_logger,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Initialize the OTAC object.
|
|
73
73
|
|
|
74
74
|
Args:
|
|
75
|
-
protocol (str):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
protocol (str):
|
|
76
|
+
Either http or https.
|
|
77
|
+
hostname (str):
|
|
78
|
+
The hostname of the Archive Center to communicate with.
|
|
79
|
+
port (int):
|
|
80
|
+
The port number used to talk to the Archive Center .
|
|
81
|
+
ds_username (str):
|
|
82
|
+
The admin user name of Archive Center (dsadmin).
|
|
83
|
+
ds_password (str):
|
|
84
|
+
The admin password of Archive Center (dsadmin).
|
|
85
|
+
admin_username (str):
|
|
86
|
+
The admin user name of Archive Center (otadmin@otds.admin).
|
|
87
|
+
admin_password (str):
|
|
88
|
+
The admin password of Archive Center (otadmin@otds.admin).
|
|
89
|
+
otds_ticket (str, optional):
|
|
90
|
+
Existing OTDS authentication ticket.
|
|
91
|
+
logger (logging.Logger, optional):
|
|
92
|
+
The logging object to use for all log messages. Defaults to default_logger.
|
|
93
|
+
|
|
82
94
|
"""
|
|
83
95
|
|
|
96
|
+
if logger != default_logger:
|
|
97
|
+
self.logger = logger.getChild("otac")
|
|
98
|
+
for logfilter in logger.filters:
|
|
99
|
+
self.logger.addFilter(logfilter)
|
|
100
|
+
|
|
84
101
|
otac_config = {}
|
|
85
102
|
|
|
86
103
|
if hostname:
|
|
@@ -132,47 +149,58 @@ class OTAC:
|
|
|
132
149
|
self._otac_ticket = otds_ticket
|
|
133
150
|
|
|
134
151
|
def config(self) -> dict:
|
|
135
|
-
"""
|
|
152
|
+
"""Return the configuration dictionary.
|
|
136
153
|
|
|
137
154
|
Returns:
|
|
138
155
|
dict: Configuration dictionary
|
|
156
|
+
|
|
139
157
|
"""
|
|
140
158
|
return self._config
|
|
141
159
|
|
|
142
160
|
def hostname(self) -> str:
|
|
143
|
-
"""
|
|
161
|
+
"""Return the Archive Center hostname.
|
|
144
162
|
|
|
145
163
|
Returns:
|
|
146
164
|
str: Archive Center hostname
|
|
165
|
+
|
|
147
166
|
"""
|
|
148
167
|
return self.config()["hostname"]
|
|
149
168
|
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
# end method definition
|
|
170
|
+
|
|
171
|
+
def set_hostname(self, hostname: str) -> None:
|
|
172
|
+
"""Set the Archive Center hostname.
|
|
152
173
|
|
|
153
174
|
Args:
|
|
154
|
-
hostname (str):
|
|
175
|
+
hostname (str):
|
|
176
|
+
The new Archive Center hostname.
|
|
177
|
+
|
|
155
178
|
"""
|
|
156
179
|
self.config()["hostname"] = hostname
|
|
157
180
|
|
|
181
|
+
# end method definition
|
|
182
|
+
|
|
158
183
|
def credentials(self) -> dict:
|
|
159
|
-
"""Get credentials (username + password)
|
|
184
|
+
"""Get credentials (username + password).
|
|
160
185
|
|
|
161
186
|
Returns:
|
|
162
187
|
dict: dictionary with username and password
|
|
188
|
+
|
|
163
189
|
"""
|
|
164
190
|
return {
|
|
165
191
|
"username": self.config()["admin_username"],
|
|
166
192
|
"password": self.config()["admin_password"],
|
|
167
193
|
}
|
|
168
194
|
|
|
195
|
+
# end method definition
|
|
196
|
+
|
|
169
197
|
def set_credentials(
|
|
170
198
|
self,
|
|
171
199
|
ds_username: str = "",
|
|
172
200
|
ds_password: str = "",
|
|
173
201
|
admin_username: str = "",
|
|
174
202
|
admin_password: str = "",
|
|
175
|
-
):
|
|
203
|
+
) -> None:
|
|
176
204
|
"""Set the credentials for Archive Center for the "ds" and "admin" users.
|
|
177
205
|
|
|
178
206
|
Args:
|
|
@@ -180,6 +208,7 @@ class OTAC:
|
|
|
180
208
|
ds_password (str, optional): non-default password of the "ds" user. Defaults to "".
|
|
181
209
|
admin_username (str, optional): non-default user name of the "admin" user. Defaults to "".
|
|
182
210
|
admin_password (str, optional): non-default password of the "admin" user. Defaults to "".
|
|
211
|
+
|
|
183
212
|
"""
|
|
184
213
|
if ds_username:
|
|
185
214
|
self.config()["ds_username"] = ds_username
|
|
@@ -201,30 +230,43 @@ class OTAC:
|
|
|
201
230
|
else:
|
|
202
231
|
self.config()["admin_password"] = ""
|
|
203
232
|
|
|
233
|
+
# end method definition
|
|
234
|
+
|
|
204
235
|
def base_url(self) -> str:
|
|
205
|
-
"""
|
|
236
|
+
"""Return the Archive Center base URL.
|
|
206
237
|
|
|
207
238
|
Returns:
|
|
208
239
|
str: Archive Center base URL
|
|
240
|
+
|
|
209
241
|
"""
|
|
242
|
+
|
|
210
243
|
return self.config()["baseUrl"]
|
|
211
244
|
|
|
245
|
+
# end method definition
|
|
246
|
+
|
|
212
247
|
def exec_url(self) -> str:
|
|
213
|
-
"""
|
|
248
|
+
"""Return the Archive Center URL to execute commands.
|
|
214
249
|
|
|
215
250
|
Returns:
|
|
216
251
|
str: Archive Center exec URL
|
|
252
|
+
|
|
217
253
|
"""
|
|
218
254
|
return self.config()["execUrl"]
|
|
219
255
|
|
|
256
|
+
# end method definition
|
|
257
|
+
|
|
220
258
|
def request_form_header(self) -> dict:
|
|
221
259
|
"""Deliver the FORM request header used for the SOAP calls.
|
|
222
|
-
|
|
260
|
+
|
|
261
|
+
Consists of Token + Form Headers (see global variable)
|
|
223
262
|
|
|
224
263
|
Args:
|
|
225
264
|
None.
|
|
265
|
+
|
|
226
266
|
Return:
|
|
227
|
-
dict:
|
|
267
|
+
dict:
|
|
268
|
+
The request header for forms content type that includes the authorization token.
|
|
269
|
+
|
|
228
270
|
"""
|
|
229
271
|
|
|
230
272
|
# create union of two dicts: cookie and headers
|
|
@@ -239,12 +281,16 @@ class OTAC:
|
|
|
239
281
|
|
|
240
282
|
def request_json_header(self) -> dict:
|
|
241
283
|
"""Deliver the JSON request header used for the CRUD REST API calls.
|
|
242
|
-
|
|
284
|
+
|
|
285
|
+
Consists of Cookie + JSON Headers (see global variable)
|
|
243
286
|
|
|
244
287
|
Args:
|
|
245
288
|
None.
|
|
289
|
+
|
|
246
290
|
Return:
|
|
247
|
-
dict:
|
|
291
|
+
dict:
|
|
292
|
+
The request header for JSON content type that includes the authorization token.
|
|
293
|
+
|
|
248
294
|
"""
|
|
249
295
|
|
|
250
296
|
if not self._otac_ticket:
|
|
@@ -253,7 +299,8 @@ class OTAC:
|
|
|
253
299
|
# create union of two dicts: cookie and headers
|
|
254
300
|
# (with Python 3.9 this would be easier with the "|" operator)
|
|
255
301
|
request_header = {}
|
|
256
|
-
|
|
302
|
+
if self._otac_ticket:
|
|
303
|
+
request_header["Authorization"] = "token " + self._otac_ticket
|
|
257
304
|
request_header.update(REQUEST_JSON_HEADERS)
|
|
258
305
|
|
|
259
306
|
return request_header
|
|
@@ -266,15 +313,22 @@ class OTAC:
|
|
|
266
313
|
additional_error_message: str = "",
|
|
267
314
|
show_error: bool = True,
|
|
268
315
|
) -> dict | None:
|
|
269
|
-
"""
|
|
270
|
-
|
|
316
|
+
"""Convert the text property of a request response object to a dictionary.
|
|
317
|
+
|
|
318
|
+
This is done in a safe way that also handles exceptions.
|
|
319
|
+
|
|
271
320
|
Args:
|
|
272
|
-
response_object (object):
|
|
273
|
-
|
|
274
|
-
|
|
321
|
+
response_object (object):
|
|
322
|
+
The reponse object delivered by the request call.
|
|
323
|
+
additional_error_message (str):
|
|
324
|
+
To print a custom error message.
|
|
325
|
+
show_error (bool):
|
|
326
|
+
If True, log an error, if False log a warning.
|
|
275
327
|
|
|
276
328
|
Returns:
|
|
277
|
-
dict:
|
|
329
|
+
dict:
|
|
330
|
+
The response or None in case of an error.
|
|
331
|
+
|
|
278
332
|
"""
|
|
279
333
|
|
|
280
334
|
if not response_object:
|
|
@@ -285,16 +339,17 @@ class OTAC:
|
|
|
285
339
|
except json.JSONDecodeError as exception:
|
|
286
340
|
if additional_error_message:
|
|
287
341
|
message = "Cannot decode response as JSon. {}; error -> {}".format(
|
|
288
|
-
additional_error_message,
|
|
342
|
+
additional_error_message,
|
|
343
|
+
exception,
|
|
289
344
|
)
|
|
290
345
|
else:
|
|
291
346
|
message = "Cannot decode response as JSon; error -> {}".format(
|
|
292
|
-
exception
|
|
347
|
+
exception,
|
|
293
348
|
)
|
|
294
349
|
if show_error:
|
|
295
|
-
logger.error(message)
|
|
350
|
+
self.logger.error(message)
|
|
296
351
|
else:
|
|
297
|
-
logger.debug(message)
|
|
352
|
+
self.logger.debug(message)
|
|
298
353
|
return None
|
|
299
354
|
else:
|
|
300
355
|
return dict_object
|
|
@@ -302,21 +357,25 @@ class OTAC:
|
|
|
302
357
|
# end method definition
|
|
303
358
|
|
|
304
359
|
def authenticate(self, revalidate: bool = False) -> dict | None:
|
|
305
|
-
"""
|
|
360
|
+
"""Authenticate at Archive Center and retrieve Ticket.
|
|
306
361
|
|
|
307
362
|
Args:
|
|
308
|
-
revalidate (bool, optional):
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
363
|
+
revalidate (bool, optional):
|
|
364
|
+
Determins if a re-athentication is enforced
|
|
365
|
+
(e.g. if session has timed out with 401 error).
|
|
366
|
+
By default we use the OTDS ticket (if exists) for the authentication with OTCS.
|
|
367
|
+
This switch allows the forced usage of username / password for the authentication.
|
|
368
|
+
|
|
312
369
|
Returns:
|
|
313
|
-
dict
|
|
314
|
-
|
|
370
|
+
dict | None:
|
|
371
|
+
Cookie information of None in case of an error.
|
|
372
|
+
Also stores cookie information in self._cookie
|
|
373
|
+
|
|
315
374
|
"""
|
|
316
375
|
|
|
317
376
|
# Already authenticated and session still valid?
|
|
318
377
|
if self._otac_ticket and not revalidate:
|
|
319
|
-
logger.debug(
|
|
378
|
+
self.logger.debug(
|
|
320
379
|
"Session still valid - return existing ticket -> %s",
|
|
321
380
|
str(self._otac_ticket),
|
|
322
381
|
)
|
|
@@ -327,7 +386,7 @@ class OTAC:
|
|
|
327
386
|
request_url = self.config()["authenticationUrl"]
|
|
328
387
|
# Check if previous authentication was not successful.
|
|
329
388
|
# Then we do the normal username + password authentication:
|
|
330
|
-
logger.debug(
|
|
389
|
+
self.logger.debug(
|
|
331
390
|
"Requesting OTAC ticket with User/Password; calling -> %s",
|
|
332
391
|
request_url,
|
|
333
392
|
)
|
|
@@ -337,32 +396,37 @@ class OTAC:
|
|
|
337
396
|
response = requests.post(
|
|
338
397
|
url=request_url,
|
|
339
398
|
data=json.dumps(
|
|
340
|
-
self.credentials()
|
|
399
|
+
self.credentials(),
|
|
341
400
|
), # this includes username + password
|
|
342
401
|
headers=REQUEST_JSON_HEADERS,
|
|
343
402
|
timeout=REQUEST_TIMEOUT,
|
|
344
403
|
)
|
|
345
404
|
except requests.exceptions.RequestException as exception:
|
|
346
|
-
logger.warning(
|
|
405
|
+
self.logger.warning(
|
|
347
406
|
"Unable to connect to -> %s; error -> %s",
|
|
348
407
|
request_url,
|
|
349
|
-
exception
|
|
408
|
+
str(exception),
|
|
350
409
|
)
|
|
351
|
-
logger.warning("OTAC service may not be ready yet.")
|
|
410
|
+
self.logger.warning("OTAC service may not be ready yet.")
|
|
352
411
|
return None
|
|
353
412
|
|
|
354
413
|
if response.ok:
|
|
355
414
|
authenticate_list = self.parse_request_response(
|
|
356
|
-
response,
|
|
415
|
+
response_object=response,
|
|
416
|
+
additional_error_message="This can be normal during restart",
|
|
417
|
+
show_error=False,
|
|
357
418
|
)
|
|
358
419
|
if not authenticate_list:
|
|
359
420
|
return None
|
|
360
421
|
else:
|
|
361
422
|
authenticate_dict = authenticate_list[1]
|
|
362
423
|
otac_ticket = authenticate_dict["TOKEN"]
|
|
363
|
-
logger.debug("Ticket -> %s", otac_ticket)
|
|
424
|
+
self.logger.debug("Ticket -> %s", otac_ticket)
|
|
364
425
|
else:
|
|
365
|
-
logger.error(
|
|
426
|
+
self.logger.error(
|
|
427
|
+
"Failed to request an OTAC ticket; error -> %s",
|
|
428
|
+
response.text,
|
|
429
|
+
)
|
|
366
430
|
return None
|
|
367
431
|
|
|
368
432
|
# Store authentication ticket:
|
|
@@ -373,12 +437,15 @@ class OTAC:
|
|
|
373
437
|
# end method definition
|
|
374
438
|
|
|
375
439
|
def authenticate_soap(self) -> str:
|
|
376
|
-
"""Authenticate via SOAP with admin User
|
|
440
|
+
"""Authenticate via SOAP with admin User.
|
|
377
441
|
|
|
378
442
|
Args:
|
|
379
443
|
None
|
|
444
|
+
|
|
380
445
|
Returns:
|
|
381
|
-
|
|
446
|
+
str:
|
|
447
|
+
The string with the SOAP token.
|
|
448
|
+
|
|
382
449
|
"""
|
|
383
450
|
|
|
384
451
|
url = self.base_url() + "/archive/services/Authentication?wsdl"
|
|
@@ -393,12 +460,16 @@ class OTAC:
|
|
|
393
460
|
# end method definition
|
|
394
461
|
|
|
395
462
|
def exec_command(self, command: str) -> dict:
|
|
396
|
-
"""Execute a command on Archive Center
|
|
463
|
+
"""Execute a command on Archive Center.
|
|
397
464
|
|
|
398
465
|
Args:
|
|
399
|
-
command (str):
|
|
466
|
+
command (str):
|
|
467
|
+
The command to execute.
|
|
468
|
+
|
|
400
469
|
Returns:
|
|
401
|
-
dict:
|
|
470
|
+
dict:
|
|
471
|
+
The response of the HTTP request.
|
|
472
|
+
|
|
402
473
|
"""
|
|
403
474
|
|
|
404
475
|
payload = {
|
|
@@ -408,17 +479,20 @@ class OTAC:
|
|
|
408
479
|
}
|
|
409
480
|
|
|
410
481
|
request_url = self.exec_url()
|
|
411
|
-
logger.info(
|
|
482
|
+
self.logger.info(
|
|
412
483
|
"Execute command -> %s on Archive Center (user -> %s); calling -> %s",
|
|
413
484
|
command,
|
|
414
485
|
payload["user"],
|
|
415
486
|
request_url,
|
|
416
487
|
)
|
|
417
488
|
response = requests.post(
|
|
418
|
-
url=request_url,
|
|
489
|
+
url=request_url,
|
|
490
|
+
data=payload,
|
|
491
|
+
headers=REQUEST_FORM_HEADERS,
|
|
492
|
+
timeout=REQUEST_TIMEOUT,
|
|
419
493
|
)
|
|
420
494
|
if not response.ok:
|
|
421
|
-
logger.error(
|
|
495
|
+
self.logger.error(
|
|
422
496
|
"Failed to execute command -> %s on Archive Center; error -> %s",
|
|
423
497
|
command,
|
|
424
498
|
response.text.replace("\n", " "), # avoid multi-line log entries
|
|
@@ -434,39 +508,50 @@ class OTAC:
|
|
|
434
508
|
logical_archive: str,
|
|
435
509
|
cert_path: str,
|
|
436
510
|
permissions: str = "rcud",
|
|
437
|
-
):
|
|
438
|
-
"""Put Certificate on Archive Center via SOAP Call
|
|
511
|
+
) -> dict | None:
|
|
512
|
+
"""Put Certificate on Archive Center via SOAP Call.
|
|
439
513
|
|
|
440
514
|
Args:
|
|
441
|
-
auth_id (str):
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
515
|
+
auth_id (str):
|
|
516
|
+
ID of Certification
|
|
517
|
+
logical_archive (str):
|
|
518
|
+
The Archive ID.
|
|
519
|
+
cert_path (str):
|
|
520
|
+
The path to local certificate file (base64 encoded).
|
|
521
|
+
permissions (str, optional):
|
|
522
|
+
Permissions of the certificate.
|
|
523
|
+
Defaults to "rcud" (read-create-update-delete).
|
|
524
|
+
|
|
446
525
|
Returns:
|
|
447
|
-
|
|
526
|
+
Response or None if the request fails.
|
|
527
|
+
|
|
448
528
|
"""
|
|
449
529
|
|
|
450
530
|
# Check if the photo file exists
|
|
451
531
|
if not os.path.isfile(cert_path):
|
|
452
|
-
logger.error("Certificate file -> '%s' not found!", cert_path)
|
|
532
|
+
self.logger.error("Certificate file -> '%s' not found!", cert_path)
|
|
453
533
|
return None
|
|
454
534
|
|
|
455
|
-
with open(file=cert_path,
|
|
535
|
+
with open(file=cert_path, encoding="utf-8") as cert_file:
|
|
456
536
|
cert_content = cert_file.read().strip()
|
|
457
537
|
|
|
458
538
|
# Check that we have the pem certificate file - this is what OTAC expects.
|
|
459
539
|
# If the file content is base64 encoded we will decode it
|
|
460
540
|
if "BEGIN CERTIFICATE" in cert_content:
|
|
461
|
-
logger.debug(
|
|
541
|
+
self.logger.debug(
|
|
542
|
+
"Certificate file -> '%s' is not base64 encoded",
|
|
543
|
+
cert_path,
|
|
544
|
+
)
|
|
462
545
|
elif "BEGIN CERTIFICATE" in base64.b64decode(
|
|
463
|
-
cert_content,
|
|
546
|
+
cert_content,
|
|
547
|
+
validate=True,
|
|
464
548
|
).decode("utf-8"):
|
|
465
|
-
logger.debug("Certificate file -> '%s' is base64 encoded", cert_path)
|
|
549
|
+
self.logger.debug("Certificate file -> '%s' is base64 encoded", cert_path)
|
|
466
550
|
cert_content = base64.b64decode(cert_content, validate=True).decode("utf-8")
|
|
467
551
|
else:
|
|
468
|
-
logger.error(
|
|
469
|
-
"Certificate file -> '%s' is not in the right format",
|
|
552
|
+
self.logger.error(
|
|
553
|
+
"Certificate file -> '%s' is not in the right format",
|
|
554
|
+
cert_path,
|
|
470
555
|
)
|
|
471
556
|
return None
|
|
472
557
|
|
|
@@ -479,7 +564,7 @@ class OTAC:
|
|
|
479
564
|
+ "&permissions="
|
|
480
565
|
+ permissions
|
|
481
566
|
)
|
|
482
|
-
logger.debug(
|
|
567
|
+
self.logger.debug(
|
|
483
568
|
"Putting certificate -> '%s' on Archive -> '%s'; calling -> %s",
|
|
484
569
|
cert_path,
|
|
485
570
|
logical_archive,
|
|
@@ -489,14 +574,14 @@ class OTAC:
|
|
|
489
574
|
url=request_url,
|
|
490
575
|
data=cert_content,
|
|
491
576
|
headers=REQUEST_FORM_HEADERS,
|
|
492
|
-
timeout=
|
|
577
|
+
timeout=REQUEST_TIMEOUT,
|
|
493
578
|
)
|
|
494
579
|
|
|
495
580
|
if not response.ok:
|
|
496
581
|
message = response.text.split(
|
|
497
|
-
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN'
|
|
582
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN',
|
|
498
583
|
)[0]
|
|
499
|
-
logger.error(
|
|
584
|
+
self.logger.error(
|
|
500
585
|
"Failed to put certificate -> '%s' on Archive -> '%s'; error -> %s",
|
|
501
586
|
cert_path,
|
|
502
587
|
logical_archive,
|
|
@@ -508,16 +593,24 @@ class OTAC:
|
|
|
508
593
|
# end method definition
|
|
509
594
|
|
|
510
595
|
def enable_cert(
|
|
511
|
-
self,
|
|
596
|
+
self,
|
|
597
|
+
auth_id: str,
|
|
598
|
+
logical_archive: str,
|
|
599
|
+
enable: bool = True,
|
|
512
600
|
) -> bool:
|
|
513
|
-
"""
|
|
601
|
+
"""Enable Certitificate on Archive Center via SOAP call.
|
|
514
602
|
|
|
515
603
|
Args:
|
|
516
|
-
auth_id (str):
|
|
517
|
-
|
|
518
|
-
|
|
604
|
+
auth_id (str):
|
|
605
|
+
The authorization ID.
|
|
606
|
+
logical_archive (str):
|
|
607
|
+
The logical archive.
|
|
608
|
+
enable (bool, optional):
|
|
609
|
+
Enable or Disable certificate. Defaults to True.
|
|
610
|
+
|
|
519
611
|
Returns:
|
|
520
612
|
True if certificate has been activated, False if an error has occured.
|
|
613
|
+
|
|
521
614
|
"""
|
|
522
615
|
|
|
523
616
|
if not self._soap_token:
|
|
@@ -546,42 +639,48 @@ class OTAC:
|
|
|
546
639
|
)
|
|
547
640
|
# With SOAP, no response is a good response!
|
|
548
641
|
if not response:
|
|
549
|
-
logger.debug("Archive Center certificate has been activated.")
|
|
642
|
+
self.logger.debug("Archive Center certificate has been activated.")
|
|
550
643
|
return True
|
|
551
644
|
elif response.code == 500:
|
|
552
|
-
logger.error(
|
|
645
|
+
self.logger.error(
|
|
553
646
|
"Failed to activate Archive Center certificate for Client -> %s on Archive -> '%s'!",
|
|
554
647
|
auth_id,
|
|
555
648
|
logical_archive,
|
|
556
649
|
)
|
|
557
650
|
return False
|
|
558
651
|
|
|
559
|
-
except WebFault
|
|
560
|
-
logger.error(
|
|
561
|
-
"Failed to execute SetCertificateFlags for Client -> %s on Archive -> '%s'
|
|
652
|
+
except WebFault:
|
|
653
|
+
self.logger.error(
|
|
654
|
+
"Failed to execute SetCertificateFlags for Client -> %s on Archive -> '%s'",
|
|
562
655
|
auth_id,
|
|
563
656
|
logical_archive,
|
|
564
|
-
exception,
|
|
565
657
|
)
|
|
566
658
|
return False
|
|
567
659
|
|
|
568
660
|
# end method definition
|
|
569
661
|
|
|
570
662
|
def enable_certificate(
|
|
571
|
-
self,
|
|
663
|
+
self,
|
|
664
|
+
cert_name: str,
|
|
665
|
+
cert_type: str,
|
|
666
|
+
logical_archive: str | None = None,
|
|
572
667
|
) -> dict | None:
|
|
573
|
-
"""Enable a certificate via the new REST API (replacing the old SOAP interface)
|
|
668
|
+
"""Enable a certificate via the new REST API (replacing the old SOAP interface).
|
|
574
669
|
|
|
575
670
|
Args:
|
|
576
|
-
cert_name (str):
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
671
|
+
cert_name (str):
|
|
672
|
+
The name of the certificate.
|
|
673
|
+
cert_type (str):
|
|
674
|
+
The type of the certificate.
|
|
675
|
+
logical_archive (str, optional):
|
|
676
|
+
Logical archive name. If empty it is a global certificate
|
|
677
|
+
for all logical archives in Archive Center.
|
|
580
678
|
|
|
581
679
|
Returns:
|
|
582
|
-
dict | None:
|
|
680
|
+
dict | None:
|
|
681
|
+
REST response or None if the request fails
|
|
583
682
|
|
|
584
|
-
|
|
683
|
+
Example:
|
|
585
684
|
{
|
|
586
685
|
'IDNO': '3',
|
|
587
686
|
'CERT_NAME': 'SP_otcs-admin-0',
|
|
@@ -593,15 +692,10 @@ class OTAC:
|
|
|
593
692
|
'CERTIFICATE': '...',
|
|
594
693
|
'PRIVILEGES': {'read': True, 'create': True, 'update': True, 'delete': True}
|
|
595
694
|
}
|
|
695
|
+
|
|
596
696
|
"""
|
|
597
697
|
|
|
598
|
-
request_url = (
|
|
599
|
-
self.config()["certUrl"]
|
|
600
|
-
+ "?cert_name="
|
|
601
|
-
+ cert_name
|
|
602
|
-
+ "&cert_type="
|
|
603
|
-
+ cert_type
|
|
604
|
-
)
|
|
698
|
+
request_url = self.config()["certUrl"] + "?cert_name=" + cert_name + "&cert_type=" + cert_type
|
|
605
699
|
if logical_archive:
|
|
606
700
|
request_url += "&assigned_archive=" + logical_archive
|
|
607
701
|
|
|
@@ -609,7 +703,7 @@ class OTAC:
|
|
|
609
703
|
|
|
610
704
|
payload = {"ENABLED": True}
|
|
611
705
|
|
|
612
|
-
logger.debug(
|
|
706
|
+
self.logger.debug(
|
|
613
707
|
"Enabling certificate -> '%s' of type -> '%s' to Archive Center; calling -> %s",
|
|
614
708
|
cert_name,
|
|
615
709
|
cert_type,
|
|
@@ -625,18 +719,18 @@ class OTAC:
|
|
|
625
719
|
timeout=REQUEST_TIMEOUT,
|
|
626
720
|
)
|
|
627
721
|
if response.ok:
|
|
628
|
-
logger.debug(
|
|
722
|
+
self.logger.debug(
|
|
629
723
|
"Certificate -> '%s' has been enabled on Archive Center keystore",
|
|
630
724
|
cert_name,
|
|
631
725
|
)
|
|
632
726
|
return self.parse_request_response(response)
|
|
633
727
|
# Check if Session has expired - then re-authenticate and try once more
|
|
634
728
|
elif response.status_code == 401 and retries == 0:
|
|
635
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
729
|
+
self.logger.debug("Session has expired - try to re-authenticate...")
|
|
636
730
|
self.authenticate(revalidate=True)
|
|
637
731
|
retries += 1
|
|
638
732
|
else:
|
|
639
|
-
logger.error(
|
|
733
|
+
self.logger.error(
|
|
640
734
|
"Failed to enable certificate -> '%s' in Archive Center; status -> %s; error -> %s",
|
|
641
735
|
cert_name,
|
|
642
736
|
response.status_code,
|