pyGuardPoint 2.1.0__tar.gz → 2.1.2__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.
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/PKG-INFO +1 -1
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/CustomWebsocketTransport.py +15 -3
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cardholders.py +10 -22
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cards.py +5 -2
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_departments.py +4 -1
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_readers.py +4 -1
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_odata_filter.py +13 -6
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_str_match_algo.py +11 -2
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/guardpoint_connection_asyncio.py +60 -42
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_connection.py +4 -3
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_dataclasses.py +25 -18
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_utils.py +17 -7
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/PKG-INFO +1 -1
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/setup.py +1 -1
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/LICENSE.txt +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/README.rst +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/CustomWebsocketTransportOld.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/__init__.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_accessgroups.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_alarmstates.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_alarmzones.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_areas.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_cardholdertypes.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_controllers.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_customizedfields.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_diagnostic.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_events.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_genericinformation.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_inputs.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_manualevents.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_ouputs.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_personaldetails.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_scheduledmags.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_securitygroups.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_sites.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/_guardpoint_weeklyprograms.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/__init__.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_accessgroups.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmstates.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmzones.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_areas.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cardholders.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cardholdertypes.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cards.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_controllers.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_customizedfields.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_departments.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_diagnostic.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_events.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_genericinformation.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_manualevents.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_ouputs.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_personaldetails.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_readers.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_scheduledmags.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_securitygroups.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_sites.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_weeklyprograms.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_asyncio.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_error.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/guardpoint_threaded.py +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/SOURCES.txt +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/dependency_links.txt +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/not-zip-safe +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/requires.txt +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint.egg-info/top_level.txt +0 -0
- {pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/setup.cfg +0 -0
|
@@ -8,10 +8,22 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
from aiohttp import ClientSession, TCPConnector
|
|
9
9
|
from aiohttp import ClientTimeout
|
|
10
10
|
from aiohttp import ServerConnectionError
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
|
|
12
|
+
# Handle websockets version compatibility
|
|
13
|
+
try:
|
|
14
|
+
from websockets.asyncio.client import ClientConnection, connect
|
|
15
|
+
except ImportError:
|
|
16
|
+
# websockets<13.0 compatibility
|
|
17
|
+
from websockets.client import WebSocketClientProtocol as ClientConnection
|
|
18
|
+
from websockets.client import connect
|
|
19
|
+
|
|
13
20
|
from websockets.exceptions import ConnectionClosed
|
|
14
|
-
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from websockets.protocol import State
|
|
24
|
+
except ImportError:
|
|
25
|
+
# websockets<13.0 compatibility
|
|
26
|
+
State = None
|
|
15
27
|
|
|
16
28
|
import pysignalr.exceptions as exceptions
|
|
17
29
|
from pysignalr.messages import CompletionMessage
|
|
@@ -298,9 +298,6 @@ class CardholdersAPI:
|
|
|
298
298
|
return None
|
|
299
299
|
else:
|
|
300
300
|
return None
|
|
301
|
-
# Part of the Cards_API
|
|
302
|
-
# (Broken API Call)
|
|
303
|
-
#return self.get_cardholder_by_card_code(card_code)
|
|
304
301
|
else:
|
|
305
302
|
return self._get_card_holder(uid)
|
|
306
303
|
|
|
@@ -355,12 +352,6 @@ class CardholdersAPI:
|
|
|
355
352
|
"securityGroup," \
|
|
356
353
|
"insideArea"
|
|
357
354
|
|
|
358
|
-
# Do not apply site filter, when looking for individuals
|
|
359
|
-
#if self.site_uid is not None:
|
|
360
|
-
# match_args = {'ownerSiteUID': self.site_uid}
|
|
361
|
-
# filter_str = _compose_filter(exact_match=match_args)
|
|
362
|
-
# url_query_params += ("&" + filter_str)
|
|
363
|
-
|
|
364
355
|
code, json_body = self.gp_json_query("GET", url=(url + url_query_params))
|
|
365
356
|
|
|
366
357
|
if code == 404: # Not Found
|
|
@@ -428,14 +419,14 @@ class CardholdersAPI:
|
|
|
428
419
|
return []
|
|
429
420
|
if limit > 50 and count is False:
|
|
430
421
|
i_offset = offset
|
|
431
|
-
|
|
422
|
+
current_offset = 0
|
|
432
423
|
batch_limit = 50
|
|
433
424
|
card_holders = []
|
|
434
|
-
while len(card_holders)
|
|
435
|
-
if
|
|
436
|
-
batch_limit = limit -
|
|
425
|
+
while len(card_holders) < limit:
|
|
426
|
+
if current_offset + batch_limit > limit:
|
|
427
|
+
batch_limit = limit - current_offset
|
|
437
428
|
if batch_limit > 0:
|
|
438
|
-
batch = self._split_get_card_holders_query(offset=
|
|
429
|
+
batch = self._split_get_card_holders_query(offset=current_offset + i_offset, limit=batch_limit,
|
|
439
430
|
search_terms=search_terms,
|
|
440
431
|
areas=areas,
|
|
441
432
|
filter_expired=filter_expired,
|
|
@@ -447,11 +438,11 @@ class CardholdersAPI:
|
|
|
447
438
|
**cardholder_kwargs)
|
|
448
439
|
if isinstance(batch, list):
|
|
449
440
|
card_holders.extend(batch)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
441
|
+
if len(batch) < batch_limit:
|
|
442
|
+
break
|
|
443
|
+
else:
|
|
444
|
+
break
|
|
445
|
+
current_offset = len(card_holders)
|
|
455
446
|
else:
|
|
456
447
|
break
|
|
457
448
|
|
|
@@ -571,9 +562,6 @@ class CardholdersAPI:
|
|
|
571
562
|
url_query_params += "$top=" + str(limit) + "&$skip=" + str(offset)
|
|
572
563
|
|
|
573
564
|
code, json_body = self.gp_json_query("GET", url=(url + url_query_params))
|
|
574
|
-
# Check response body is formatted correctly
|
|
575
|
-
# if json_body:
|
|
576
|
-
# GuardPointResponse.check_odata_body_structure(json_body)
|
|
577
565
|
|
|
578
566
|
if code != 200:
|
|
579
567
|
error_msg = GuardPointResponse.extract_error_msg(json_body)
|
|
@@ -72,7 +72,7 @@ class CardsAPI:
|
|
|
72
72
|
if code == 401:
|
|
73
73
|
raise GuardPointUnauthorized(f"Unauthorized - ({error_msg})")
|
|
74
74
|
elif code == 404: # Not Found
|
|
75
|
-
raise GuardPointError(f"
|
|
75
|
+
raise GuardPointError(f"Cards Not Found")
|
|
76
76
|
else:
|
|
77
77
|
raise GuardPointError(f"{error_msg}")
|
|
78
78
|
|
|
@@ -244,7 +244,10 @@ class CardsAPI:
|
|
|
244
244
|
if 'value' not in json_body:
|
|
245
245
|
raise GuardPointError("Badly formatted response.")
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
value = json_body['value']
|
|
248
|
+
if isinstance(value, list):
|
|
249
|
+
return Card(value[0]) if value else None
|
|
250
|
+
return Card(value)
|
|
248
251
|
|
|
249
252
|
def get_cardholder_by_card_code(self, card_code):
|
|
250
253
|
"""
|
|
@@ -33,7 +33,10 @@ class DepartmentsAPI:
|
|
|
33
33
|
if 'value' not in json_body:
|
|
34
34
|
raise GuardPointError("Badly formatted response.")
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
value = json_body['value']
|
|
37
|
+
if isinstance(value, list):
|
|
38
|
+
return Department(value[0]) if value else None
|
|
39
|
+
return Department(value)
|
|
37
40
|
|
|
38
41
|
def get_departments(self):
|
|
39
42
|
url = "/odata/API_Departments"
|
|
@@ -141,5 +141,8 @@ class ReadersAPI:
|
|
|
141
141
|
if 'value' not in json_body:
|
|
142
142
|
raise GuardPointError("Badly formatted response.")
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
value = json_body['value']
|
|
145
|
+
if isinstance(value, list):
|
|
146
|
+
return Reader(value[0]) if value else None
|
|
147
|
+
return Reader(value)
|
|
145
148
|
|
|
@@ -93,29 +93,36 @@ def _compose_filter(search_words=None,
|
|
|
93
93
|
if isinstance(v, str):
|
|
94
94
|
v = v.replace("'", "''")
|
|
95
95
|
if validators.uuid(v):
|
|
96
|
-
filter_phrases.append(f"({k}%20eq%20{
|
|
96
|
+
filter_phrases.append(f"({k}%20eq%20{v})")
|
|
97
97
|
else:
|
|
98
98
|
filter_phrases.append(f"({k}%20eq%20'{quote(v)}')")
|
|
99
99
|
if isinstance(v, (bool, int)):
|
|
100
|
-
filter_phrases.append(f"({k}%20eq%20{
|
|
100
|
+
filter_phrases.append(f"({k}%20eq%20{v})")
|
|
101
101
|
if k == "cardholderPersonalDetail" and v.__class__.__name__ == "CardholderPersonalDetail":
|
|
102
102
|
details = v.dict(changed_only=True)
|
|
103
103
|
for dk, dv in details.items():
|
|
104
104
|
if isinstance(dv, str):
|
|
105
105
|
dv = dv.replace("'", "''")
|
|
106
|
-
|
|
106
|
+
filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20'{quote(dv)}')")
|
|
107
|
+
elif isinstance(dv, (bool, int)):
|
|
108
|
+
filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20{dv})")
|
|
109
|
+
elif isinstance(dv, type(None)):
|
|
110
|
+
filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20null)")
|
|
111
|
+
else:
|
|
112
|
+
dv_str = str(dv).replace("'", "''")
|
|
113
|
+
filter_phrases.append(f"(CardholderPersonalDetail/{dk}%20eq%20'{quote(dv_str)}')")
|
|
107
114
|
if k == "cardholderCustomizedField" and v.__class__.__name__ == "CardholderCustomizedField":
|
|
108
115
|
details = v.dict(changed_only=True)
|
|
109
116
|
for dk, dv in details.items():
|
|
110
117
|
if isinstance(dv, type(None)):
|
|
111
|
-
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%
|
|
118
|
+
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20null)")
|
|
112
119
|
elif isinstance(dv, (bool, int)):
|
|
113
120
|
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20{str(dv).lower()})")
|
|
114
121
|
elif isinstance(dv, str):
|
|
115
122
|
dv = dv.replace("'", "''")
|
|
116
123
|
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{quote(dv)}')")
|
|
117
124
|
else:
|
|
118
|
-
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{
|
|
125
|
+
filter_phrases.append(f"(CardholderCustomizedField/{dk}%20eq%20'{str(dv)}')")
|
|
119
126
|
|
|
120
127
|
if earliest_last_pass:
|
|
121
128
|
if isinstance(earliest_last_pass, datetime):
|
|
@@ -136,7 +143,7 @@ def _compose_filter(search_words=None,
|
|
|
136
143
|
if areas:
|
|
137
144
|
if isinstance(areas, Area):
|
|
138
145
|
filter_phrases.append(f"(insideAreaUID%20eq%20{areas.uid})")
|
|
139
|
-
|
|
146
|
+
elif isinstance(areas, list):
|
|
140
147
|
if len(areas) > 0:
|
|
141
148
|
area_phrases = []
|
|
142
149
|
for area in areas:
|
|
@@ -12,11 +12,20 @@ def fuzzy_match(search_words: str, cardholders: list, threshold: int = 75):
|
|
|
12
12
|
|
|
13
13
|
match_ratios = process.extract(search_words, cardholder_patterns, scorer=fuzz.WRatio, limit=20)
|
|
14
14
|
|
|
15
|
+
pattern_to_indices = {}
|
|
16
|
+
for idx, pattern in enumerate(cardholder_patterns):
|
|
17
|
+
if pattern not in pattern_to_indices:
|
|
18
|
+
pattern_to_indices[pattern] = []
|
|
19
|
+
pattern_to_indices[pattern].append(idx)
|
|
20
|
+
|
|
15
21
|
sorted_cardholders = []
|
|
22
|
+
seen = set()
|
|
16
23
|
for match in match_ratios:
|
|
17
24
|
if match[1] >= threshold:
|
|
18
|
-
pos
|
|
19
|
-
|
|
25
|
+
for pos in pattern_to_indices.get(match[0], []):
|
|
26
|
+
if pos not in seen:
|
|
27
|
+
sorted_cardholders.append(cardholders[pos])
|
|
28
|
+
seen.add(pos)
|
|
20
29
|
|
|
21
30
|
return sorted_cardholders
|
|
22
31
|
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/guardpoint_connection_asyncio.py
RENAMED
|
@@ -59,9 +59,10 @@ class GuardPointConnection:
|
|
|
59
59
|
if self.session:
|
|
60
60
|
await self.session.close()
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
def reopen(self):
|
|
63
63
|
conn = aiohttp.TCPConnector(ssl_context=self.ssl_context)
|
|
64
64
|
self.session = aiohttp.ClientSession(connector=conn)
|
|
65
|
+
|
|
65
66
|
def open(self, url_components, auth, user, pwd, key, token=None,
|
|
66
67
|
cert_file=None, key_file=None, key_pwd="", ca_file=None, p12_file=None, p12_pwd="", timeout=5):
|
|
67
68
|
self.ssl_context = None
|
|
@@ -116,7 +117,8 @@ class GuardPointConnection:
|
|
|
116
117
|
|
|
117
118
|
if cert_file and key_file:
|
|
118
119
|
# Loading of client certificate
|
|
119
|
-
|
|
120
|
+
pwd_bytes = key_pwd.encode() if isinstance(key_pwd, str) else key_pwd
|
|
121
|
+
self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=pwd_bytes if pwd_bytes else None)
|
|
120
122
|
|
|
121
123
|
if ca_file:
|
|
122
124
|
# Loading of CA certificate.
|
|
@@ -172,11 +174,11 @@ class GuardPointConnection:
|
|
|
172
174
|
if code != 200:
|
|
173
175
|
return code, auth_body
|
|
174
176
|
if self.auto_renew:
|
|
175
|
-
if self.token_expiry < (time.time()
|
|
177
|
+
if self.token_expiry < (time.time() + (20 * 60)) and self.token_expiry > time.time():
|
|
176
178
|
code, auth_body = await self._renew_token()
|
|
177
179
|
if code != 200:
|
|
178
180
|
return code, auth_body
|
|
179
|
-
|
|
181
|
+
elif self.token_expiry < time.time():
|
|
180
182
|
code, auth_body = await self._new_token()
|
|
181
183
|
if code != 200:
|
|
182
184
|
return code, auth_body
|
|
@@ -220,54 +222,70 @@ class GuardPointConnection:
|
|
|
220
222
|
json_body = None
|
|
221
223
|
except InvalidURL as e:
|
|
222
224
|
log.error(str(e))
|
|
223
|
-
return
|
|
225
|
+
return 400, {"error": str(e)}
|
|
224
226
|
except Exception as e:
|
|
225
227
|
log.error(str(e))
|
|
226
|
-
return
|
|
228
|
+
return 500, {"error": str(e)}
|
|
227
229
|
|
|
228
230
|
elif method.lower() == "post":
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
try:
|
|
232
|
+
async with self.session.post(url, data=raw_body, headers=headers) as response:
|
|
233
|
+
body = await response.text()
|
|
234
|
+
try:
|
|
235
|
+
json_body = json.loads(body)
|
|
236
|
+
except JSONDecodeError:
|
|
237
|
+
json_body = None
|
|
238
|
+
except Exception as e:
|
|
239
|
+
log.error(e)
|
|
240
|
+
json_body = None
|
|
241
|
+
except Exception as e:
|
|
242
|
+
log.error(str(e))
|
|
243
|
+
return 500, {"error": str(e)}
|
|
238
244
|
|
|
239
245
|
elif method.lower() == "patch":
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
try:
|
|
247
|
+
async with self.session.patch(url, data=raw_body, headers=headers) as response:
|
|
248
|
+
body = await response.text()
|
|
249
|
+
try:
|
|
250
|
+
json_body = json.loads(body)
|
|
251
|
+
except JSONDecodeError:
|
|
252
|
+
json_body = None
|
|
253
|
+
except Exception as e:
|
|
254
|
+
log.error(e)
|
|
255
|
+
json_body = None
|
|
256
|
+
except Exception as e:
|
|
257
|
+
log.error(str(e))
|
|
258
|
+
return 500, {"error": str(e)}
|
|
249
259
|
|
|
250
260
|
elif method.lower() == "delete":
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
try:
|
|
262
|
+
async with self.session.delete(url, data=raw_body, headers=headers) as response:
|
|
263
|
+
body = await response.text()
|
|
264
|
+
try:
|
|
265
|
+
json_body = json.loads(body)
|
|
266
|
+
except JSONDecodeError:
|
|
267
|
+
json_body = None
|
|
268
|
+
except Exception as e:
|
|
269
|
+
log.error(e)
|
|
270
|
+
json_body = None
|
|
271
|
+
except Exception as e:
|
|
272
|
+
log.error(str(e))
|
|
273
|
+
return 500, {"error": str(e)}
|
|
260
274
|
|
|
261
275
|
elif method.lower() == "put":
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
276
|
+
try:
|
|
277
|
+
async with self.session.put(url, data=raw_body, headers=headers) as response:
|
|
278
|
+
body = await response.text()
|
|
279
|
+
try:
|
|
280
|
+
json_body = json.loads(body)
|
|
281
|
+
except JSONDecodeError:
|
|
282
|
+
json_body = None
|
|
283
|
+
except Exception as e:
|
|
284
|
+
log.error(e)
|
|
285
|
+
json_body = None
|
|
286
|
+
except Exception as e:
|
|
287
|
+
log.error(str(e))
|
|
288
|
+
return 500, {"error": str(e)}
|
|
271
289
|
|
|
272
290
|
else:
|
|
273
291
|
raise ValueError("Method Not Supported")
|
|
@@ -108,7 +108,8 @@ class GuardPointConnection:
|
|
|
108
108
|
|
|
109
109
|
if cert_file and key_file:
|
|
110
110
|
# Loading of client certificate
|
|
111
|
-
|
|
111
|
+
pwd_bytes = p12_pwd.encode() if isinstance(p12_pwd, str) else p12_pwd
|
|
112
|
+
self.ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=pwd_bytes if pwd_bytes else None)
|
|
112
113
|
|
|
113
114
|
if ca_file:
|
|
114
115
|
# Loading of CA certificate.
|
|
@@ -185,11 +186,11 @@ class GuardPointConnection:
|
|
|
185
186
|
if code != 200:
|
|
186
187
|
return code, auth_body
|
|
187
188
|
if self.auto_renew:
|
|
188
|
-
if self.token_expiry < (time.time()
|
|
189
|
+
if self.token_expiry < (time.time() + (20 * 60)) and self.token_expiry > time.time():
|
|
189
190
|
code, auth_body = self._renew_token()
|
|
190
191
|
if code != 200:
|
|
191
192
|
return code, auth_body
|
|
192
|
-
|
|
193
|
+
elif self.token_expiry < time.time():
|
|
193
194
|
code, auth_body = self._new_token()
|
|
194
195
|
if code != 200:
|
|
195
196
|
return code, auth_body
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import threading
|
|
2
3
|
from collections import defaultdict
|
|
3
4
|
from dataclasses import dataclass, asdict, field
|
|
4
5
|
from enum import Enum
|
|
@@ -18,20 +19,21 @@ class EventOrder(Enum):
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class Observable:
|
|
21
|
-
# A set of all attributes which get changed
|
|
22
|
-
changed_attributes = set()
|
|
23
|
-
|
|
24
22
|
def __init__(self):
|
|
25
|
-
|
|
23
|
+
super().__setattr__('changed_attributes', set())
|
|
24
|
+
super().__setattr__('observed', defaultdict(list))
|
|
25
|
+
super().__setattr__('_observer_lock', threading.RLock())
|
|
26
26
|
|
|
27
27
|
def __setattr__(self, name, value):
|
|
28
28
|
super().__setattr__(name, value)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
observer(name)
|
|
30
|
+
with self._observer_lock:
|
|
31
|
+
for observer in self.observed.get(name, []):
|
|
32
|
+
observer(name)
|
|
32
33
|
|
|
33
34
|
def add_observer(self, name):
|
|
34
|
-
self.
|
|
35
|
+
with self._observer_lock:
|
|
36
|
+
self.observed[name].append(lambda attr_name: self.changed_attributes.add(attr_name))
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
def sanitise_args(obj: Observable, args, kwargs):
|
|
@@ -54,12 +56,12 @@ def sanitise_args(obj: Observable, args, kwargs):
|
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
class SortAlgorithm(Enum):
|
|
57
|
-
SERVER_DEFAULT = 0
|
|
59
|
+
SERVER_DEFAULT = 0
|
|
58
60
|
FUZZY_MATCH = 1
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
class CardholderOrderBy(Enum):
|
|
62
|
-
fromDateValid_DESC = 0
|
|
64
|
+
fromDateValid_DESC = 0
|
|
63
65
|
lastPassDate_DESC = 1
|
|
64
66
|
|
|
65
67
|
|
|
@@ -97,6 +99,7 @@ class CustomizedField:
|
|
|
97
99
|
cf_dict[k] = None
|
|
98
100
|
else:
|
|
99
101
|
cf_dict[k] = str(v)
|
|
102
|
+
return cf_dict
|
|
100
103
|
|
|
101
104
|
@dataclass
|
|
102
105
|
class WeeklyProgram:
|
|
@@ -122,6 +125,7 @@ class WeeklyProgram:
|
|
|
122
125
|
me_dict[k] = None
|
|
123
126
|
else:
|
|
124
127
|
me_dict[k] = str(v)
|
|
128
|
+
return me_dict
|
|
125
129
|
|
|
126
130
|
|
|
127
131
|
@dataclass
|
|
@@ -148,6 +152,7 @@ class ManualEvent:
|
|
|
148
152
|
me_dict[k] = None
|
|
149
153
|
else:
|
|
150
154
|
me_dict[k] = str(v)
|
|
155
|
+
return me_dict
|
|
151
156
|
|
|
152
157
|
|
|
153
158
|
@dataclass
|
|
@@ -156,12 +161,12 @@ class Input:
|
|
|
156
161
|
logicalStatus: str = ""
|
|
157
162
|
isUnderAlarm: bool = False
|
|
158
163
|
uid: str = ""
|
|
159
|
-
number: int = 0
|
|
164
|
+
number: int = 0
|
|
160
165
|
name: str = ""
|
|
161
166
|
descriprion: any = None
|
|
162
167
|
weeklyProgramUID: any = None
|
|
163
168
|
delayType: str = ""
|
|
164
|
-
delayTime: int = 0
|
|
169
|
+
delayTime: int = 0
|
|
165
170
|
inputType: str = ""
|
|
166
171
|
statusType: str = ""
|
|
167
172
|
controllerUID: str = ""
|
|
@@ -170,12 +175,12 @@ class Input:
|
|
|
170
175
|
lastEventType: any = None
|
|
171
176
|
latestAction: any = None
|
|
172
177
|
inputGroupUID: any = None
|
|
173
|
-
alarmPriority: int = 0
|
|
178
|
+
alarmPriority: int = 0
|
|
174
179
|
isArm: bool = False
|
|
175
180
|
isBypassed: bool = False
|
|
176
181
|
instructions: any = None
|
|
177
182
|
isGalaxy: bool = False
|
|
178
|
-
omitted: int = 0
|
|
183
|
+
omitted: int = 0
|
|
179
184
|
apiKey: any = None
|
|
180
185
|
|
|
181
186
|
def __init__(self, *args, **kwargs):
|
|
@@ -195,6 +200,7 @@ class Input:
|
|
|
195
200
|
input_dict[k] = None
|
|
196
201
|
else:
|
|
197
202
|
input_dict[k] = str(v)
|
|
203
|
+
return input_dict
|
|
198
204
|
|
|
199
205
|
|
|
200
206
|
@dataclass
|
|
@@ -231,6 +237,7 @@ class AlarmState:
|
|
|
231
237
|
alarm_state_dict[k] = None
|
|
232
238
|
else:
|
|
233
239
|
alarm_state_dict[k] = str(v)
|
|
240
|
+
return alarm_state_dict
|
|
234
241
|
|
|
235
242
|
|
|
236
243
|
@dataclass
|
|
@@ -542,7 +549,7 @@ class AccessEvent:
|
|
|
542
549
|
accessDeniedCode: str = ""
|
|
543
550
|
cardCode: str = ""
|
|
544
551
|
cardholderFirstName: any = None
|
|
545
|
-
cardholderIdNumber:
|
|
552
|
+
cardholderIdNumber: any = None
|
|
546
553
|
cardholderLastName: any = None
|
|
547
554
|
cardholderTypeName: any = None
|
|
548
555
|
cardholderTypeUID: any = None
|
|
@@ -866,7 +873,7 @@ class ScheduledMag(Observable):
|
|
|
866
873
|
@dataclass
|
|
867
874
|
class CardholderCustomizedField(Observable):
|
|
868
875
|
uid: str = ""
|
|
869
|
-
cF_BoolField_1: bool = False
|
|
876
|
+
cF_BoolField_1: bool = False
|
|
870
877
|
cF_BoolField_2: bool = False
|
|
871
878
|
cF_BoolField_3: bool = False
|
|
872
879
|
cF_BoolField_4: bool = False
|
|
@@ -1181,8 +1188,8 @@ class Cardholder(Observable):
|
|
|
1181
1188
|
|
|
1182
1189
|
for property_name in cardholder_dict:
|
|
1183
1190
|
if isinstance(cardholder_dict[property_name], list):
|
|
1184
|
-
if property_name == "accessGroupUIDs":
|
|
1185
|
-
setattr(self, property_name,
|
|
1191
|
+
if property_name == "accessGroupUIDs" or property_name == "liftAccessGroupUIDs":
|
|
1192
|
+
setattr(self, property_name, cardholder_dict[property_name])
|
|
1186
1193
|
elif property_name == "cards":
|
|
1187
1194
|
setattr(self, property_name, [])
|
|
1188
1195
|
for card_entry in cardholder_dict[property_name]:
|
|
@@ -1219,7 +1226,7 @@ class Cardholder(Observable):
|
|
|
1219
1226
|
try:
|
|
1220
1227
|
if isinstance(getattr(self, property_name), str):
|
|
1221
1228
|
value = str(value)
|
|
1222
|
-
except:
|
|
1229
|
+
except AttributeError:
|
|
1223
1230
|
pass
|
|
1224
1231
|
|
|
1225
1232
|
|
|
@@ -6,19 +6,29 @@ from .guardpoint_error import GuardPointError
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def url_parser(url):
|
|
9
|
+
if not url or not isinstance(url, str):
|
|
10
|
+
raise GuardPointError("Invalid URL: must be a non-empty string")
|
|
11
|
+
|
|
9
12
|
parts = urlparse(url)
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
if not parts.netloc:
|
|
15
|
+
raise GuardPointError("Invalid URL: missing host")
|
|
16
|
+
|
|
17
|
+
directories = parts.path.strip('/').split('/') if parts.path else ['']
|
|
18
|
+
queries = parts.query.strip('&').split('&') if parts.query else []
|
|
12
19
|
host = parts.netloc.strip(':').split(':')[0]
|
|
13
20
|
|
|
21
|
+
if not host:
|
|
22
|
+
raise GuardPointError("Invalid URL: empty host")
|
|
23
|
+
|
|
14
24
|
elements = {
|
|
15
|
-
'scheme': parts.scheme,
|
|
25
|
+
'scheme': parts.scheme or '',
|
|
16
26
|
'host': host,
|
|
17
|
-
'path': parts.path,
|
|
18
|
-
'params': parts.params,
|
|
19
|
-
'query': parts.query,
|
|
27
|
+
'path': parts.path or '',
|
|
28
|
+
'params': parts.params or '',
|
|
29
|
+
'query': parts.query or '',
|
|
20
30
|
'port': parts.port,
|
|
21
|
-
'fragment': parts.fragment,
|
|
31
|
+
'fragment': parts.fragment or '',
|
|
22
32
|
'directories': directories,
|
|
23
33
|
'queries': queries,
|
|
24
34
|
}
|
|
@@ -5,7 +5,7 @@ long_description = open('README.rst').read()
|
|
|
5
5
|
setup(name="pyGuardPoint",
|
|
6
6
|
python_requires='>3.9.0',
|
|
7
7
|
packages=find_packages(),
|
|
8
|
-
version="2.1.
|
|
8
|
+
version="2.1.2",
|
|
9
9
|
author="John Owen",
|
|
10
10
|
description="Python wrapper for GuardPoint 10 Access Control System",
|
|
11
11
|
long_description_content_type='text/markdown',
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_accessgroups.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmstates.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_alarmzones.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_areas.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cardholders.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_cards.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_controllers.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_departments.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_diagnostic.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_events.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_manualevents.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_ouputs.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_readers.py
RENAMED
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_scheduledmags.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyguardpoint-2.1.0 → pyguardpoint-2.1.2}/pyGuardPoint/gp_asyncio/_async_guardpoint_sites.py
RENAMED
|
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
|