okta-expression-parser 0.2.0__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.
- okta_expression_parser-0.2.0/PKG-INFO +15 -0
- okta_expression_parser-0.2.0/README.md +1 -0
- okta_expression_parser-0.2.0/okta_expression_parser/__init__.py +0 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/__init__.py +5 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/arrays.py +57 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/convert.py +15 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/groups.py +25 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/iso3166convert.py +470 -0
- okta_expression_parser-0.2.0/okta_expression_parser/expression_classes/string.py +111 -0
- okta_expression_parser-0.2.0/okta_expression_parser/lexer.py +68 -0
- okta_expression_parser-0.2.0/okta_expression_parser/parser.py +316 -0
- okta_expression_parser-0.2.0/pyproject.toml +21 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: okta-expression-parser
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Returns a boolean by passing Okta profile data to an Okta Expression using Okta Expression Language
|
|
5
|
+
Author: Mathew Moon
|
|
6
|
+
Author-email: me@mathewmoon.net
|
|
7
|
+
Requires-Python: >=3.11,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: sly (>=0.5,<0.6)
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Okta expression parser
|
|
15
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Okta expression parser
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import Any, List
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _ArrayType(list):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Arrays:
|
|
9
|
+
@classmethod
|
|
10
|
+
def contains(cls, array: _ArrayType, val: str) -> bool:
|
|
11
|
+
"""Tests if a value exists in an expression's array."""
|
|
12
|
+
return val in array
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def add(cls, array: _ArrayType, val: Any) -> _ArrayType:
|
|
16
|
+
"""Appends an element to a list and returns the list."""
|
|
17
|
+
return _ArrayType(array + [val])
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def remove(cls, array: _ArrayType, val: Any) -> _ArrayType:
|
|
21
|
+
"""Removes all occurances of a value from a list."""
|
|
22
|
+
return _ArrayType([x for x in array if x != val])
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def clear(cls, _: _ArrayType) -> _ArrayType:
|
|
26
|
+
"""Returns and empty list."""
|
|
27
|
+
return _ArrayType([])
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get(cls, array: _ArrayType, index: int) -> Any:
|
|
31
|
+
"""Returns element at `index`. If `index` does not exist then returns None."""
|
|
32
|
+
try:
|
|
33
|
+
return array[index]
|
|
34
|
+
except IndexError:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def flatten(cls, *args: _ArrayType) -> _ArrayType:
|
|
39
|
+
"""Returns a flattened list from all args."""
|
|
40
|
+
return _ArrayType(
|
|
41
|
+
[item for row in args for item in (row if isinstance(row, list) else [row])]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def size(cls, array: _ArrayType) -> int:
|
|
46
|
+
"""Returns the number of elements in array."""
|
|
47
|
+
return len(array)
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def isEmpty(cls, array: _ArrayType) -> bool:
|
|
51
|
+
"""Returns if an array is empty or not."""
|
|
52
|
+
return not array
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def toCsvString(cls, array: _ArrayType) -> str:
|
|
56
|
+
"""Returns a comma delineated string from array elements."""
|
|
57
|
+
return ",".join(array)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Convert:
|
|
2
|
+
@classmethod
|
|
3
|
+
def toInt(val: str | float) -> int:
|
|
4
|
+
"""Casts a string to an int."""
|
|
5
|
+
try:
|
|
6
|
+
return int(val)
|
|
7
|
+
except ValueError:
|
|
8
|
+
return 0
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def toNum(val: str) -> float:
|
|
12
|
+
try:
|
|
13
|
+
return float(val)
|
|
14
|
+
except ValueError:
|
|
15
|
+
return 0
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .arrays import _ArrayType
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Groups:
|
|
5
|
+
group_data = {}
|
|
6
|
+
|
|
7
|
+
@classmethod
|
|
8
|
+
def getFilteredGroups(
|
|
9
|
+
cls, allow_list: _ArrayType, group_expression: str, limit: int = 5
|
|
10
|
+
) -> _ArrayType:
|
|
11
|
+
"""
|
|
12
|
+
Returns all groups that are in `allow_list`, which is a list of ID's AND the user is a member of. The return value is
|
|
13
|
+
the key specified by `group_expression`. For example, if `group_expression` is "user.name" then
|
|
14
|
+
a list of group names will be returned.
|
|
15
|
+
"""
|
|
16
|
+
group_key = group_expression.split(".")[-1]
|
|
17
|
+
res = [cls.group_data.get(x, {}).get(group_key) for x in allow_list]
|
|
18
|
+
|
|
19
|
+
if not res:
|
|
20
|
+
return _ArrayType(res)
|
|
21
|
+
|
|
22
|
+
if len(res) > limit:
|
|
23
|
+
res = res[0:limit]
|
|
24
|
+
|
|
25
|
+
return _ArrayType([x for x in res if x is not None])
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Iso3166Convert:
|
|
5
|
+
@classmethod
|
|
6
|
+
def __get_identifier_type(cls, identifier: str) -> str:
|
|
7
|
+
if len(identifier) > 3:
|
|
8
|
+
return "name"
|
|
9
|
+
if len(identifier) == 2:
|
|
10
|
+
return "alpha2"
|
|
11
|
+
try:
|
|
12
|
+
int(identifier)
|
|
13
|
+
return "numeric"
|
|
14
|
+
except ValueError:
|
|
15
|
+
return "alpha3"
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def __get_country(cls, identifier: str) -> Dict[str, str]:
|
|
19
|
+
"""Returns a country's dict, looking it up by an identifier and what type of identifier it is."""
|
|
20
|
+
if not isinstance(identifier, str):
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
id_type = cls.__get_identifier_type(identifier)
|
|
24
|
+
country = [x for x in codes if x[id_type].lower() == identifier.lower()]
|
|
25
|
+
|
|
26
|
+
if country:
|
|
27
|
+
return country[0]
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def toAlpha2(cls, identifier: str) -> str:
|
|
31
|
+
if res := cls.__get_country(identifier):
|
|
32
|
+
return res["alpha2"]
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def toAlpha3(cls, identifier: str) -> str:
|
|
36
|
+
if res := cls.__get_country(identifier):
|
|
37
|
+
return res["alpha3"]
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def toNumeric(cls, identifier: str) -> str:
|
|
41
|
+
if res := cls.__get_country(identifier):
|
|
42
|
+
return res["numeric"]
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def toName(cls, identifier: str) -> str:
|
|
46
|
+
if res := cls.__get_country(identifier):
|
|
47
|
+
return res["name"]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
codes = [
|
|
51
|
+
{"name": "Andorra", "alpha2": "AD", "alpha3": "AND", "numeric": "020"},
|
|
52
|
+
{"name": "United Arab Emirates", "alpha2": "AE", "alpha3": "ARE", "numeric": "784"},
|
|
53
|
+
{"name": "Afghanistan", "alpha2": "AF", "alpha3": "AFG", "numeric": "004"},
|
|
54
|
+
{"name": "Antigua and Barbuda", "alpha2": "AG", "alpha3": "ATG", "numeric": "028"},
|
|
55
|
+
{"name": "Anguilla", "alpha2": "AI", "alpha3": "AIA", "numeric": "660"},
|
|
56
|
+
{"name": "Albania", "alpha2": "AL", "alpha3": "ALB", "numeric": "008"},
|
|
57
|
+
{"name": "Armenia", "alpha2": "AM", "alpha3": "ARM", "numeric": "051"},
|
|
58
|
+
{"name": "Angola", "alpha2": "AO", "alpha3": "AGO", "numeric": "024"},
|
|
59
|
+
{"name": "Antarctica", "alpha2": "AQ", "alpha3": "ATA", "numeric": "010"},
|
|
60
|
+
{"name": "Argentina", "alpha2": "AR", "alpha3": "ARG", "numeric": "032"},
|
|
61
|
+
{"name": "American Samoa", "alpha2": "AS", "alpha3": "ASM", "numeric": "016"},
|
|
62
|
+
{"name": "Austria", "alpha2": "AT", "alpha3": "AUT", "numeric": "040"},
|
|
63
|
+
{"name": "Australia", "alpha2": "AU", "alpha3": "AUS", "numeric": "036"},
|
|
64
|
+
{"name": "Aruba", "alpha2": "AW", "alpha3": "ABW", "numeric": "533"},
|
|
65
|
+
{"name": "Åland Islands", "alpha2": "AX", "alpha3": "ALA", "numeric": "248"},
|
|
66
|
+
{"name": "Azerbaijan", "alpha2": "AZ", "alpha3": "AZE", "numeric": "031"},
|
|
67
|
+
{
|
|
68
|
+
"name": "Bosnia and Herzegovina",
|
|
69
|
+
"alpha2": "BA",
|
|
70
|
+
"alpha3": "BIH",
|
|
71
|
+
"numeric": "070",
|
|
72
|
+
},
|
|
73
|
+
{"name": "Barbados", "alpha2": "BB", "alpha3": "BRB", "numeric": "052"},
|
|
74
|
+
{"name": "Bangladesh", "alpha2": "BD", "alpha3": "BGD", "numeric": "050"},
|
|
75
|
+
{"name": "Belgium", "alpha2": "BE", "alpha3": "BEL", "numeric": "056"},
|
|
76
|
+
{"name": "Burkina Faso", "alpha2": "BF", "alpha3": "BFA", "numeric": "854"},
|
|
77
|
+
{"name": "Bulgaria", "alpha2": "BG", "alpha3": "BGR", "numeric": "100"},
|
|
78
|
+
{"name": "Bahrain", "alpha2": "BH", "alpha3": "BHR", "numeric": "048"},
|
|
79
|
+
{"name": "Burundi", "alpha2": "BI", "alpha3": "BDI", "numeric": "108"},
|
|
80
|
+
{"name": "Benin", "alpha2": "BJ", "alpha3": "BEN", "numeric": "204"},
|
|
81
|
+
{"name": "Saint Barthélemy", "alpha2": "BL", "alpha3": "BLM", "numeric": "652"},
|
|
82
|
+
{"name": "Bermuda", "alpha2": "BM", "alpha3": "BMU", "numeric": "060"},
|
|
83
|
+
{"name": "Brunei Darussalam", "alpha2": "BN", "alpha3": "BRN", "numeric": "096"},
|
|
84
|
+
{
|
|
85
|
+
"name": "Bolivia (Plurinational State of)",
|
|
86
|
+
"alpha2": "BO",
|
|
87
|
+
"alpha3": "BOL",
|
|
88
|
+
"numeric": "068",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "Bonaire, Sint Eustatius and Saba",
|
|
92
|
+
"alpha2": "BQ",
|
|
93
|
+
"alpha3": "BES",
|
|
94
|
+
"numeric": "535",
|
|
95
|
+
},
|
|
96
|
+
{"name": "Brazil", "alpha2": "BR", "alpha3": "BRA", "numeric": "076"},
|
|
97
|
+
{"name": "Bahamas", "alpha2": "BS", "alpha3": "BHS", "numeric": "044"},
|
|
98
|
+
{"name": "Bhutan", "alpha2": "BT", "alpha3": "BTN", "numeric": "064"},
|
|
99
|
+
{"name": "Bouvet Island", "alpha2": "BV", "alpha3": "BVT", "numeric": "074"},
|
|
100
|
+
{"name": "Botswana", "alpha2": "BW", "alpha3": "BWA", "numeric": "072"},
|
|
101
|
+
{"name": "Belarus", "alpha2": "BY", "alpha3": "BLR", "numeric": "112"},
|
|
102
|
+
{"name": "Belize", "alpha2": "BZ", "alpha3": "BLZ", "numeric": "084"},
|
|
103
|
+
{"name": "Canada", "alpha2": "CA", "alpha3": "CAN", "numeric": "124"},
|
|
104
|
+
{
|
|
105
|
+
"name": "Cocos (Keeling) Islands",
|
|
106
|
+
"alpha2": "CC",
|
|
107
|
+
"alpha3": "CCK",
|
|
108
|
+
"numeric": "166",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "Congo, Democratic Republic of the",
|
|
112
|
+
"alpha2": "CD",
|
|
113
|
+
"alpha3": "COD",
|
|
114
|
+
"numeric": "180",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "Central African Republic",
|
|
118
|
+
"alpha2": "CF",
|
|
119
|
+
"alpha3": "CAF",
|
|
120
|
+
"numeric": "140",
|
|
121
|
+
},
|
|
122
|
+
{"name": "Congo", "alpha2": "CG", "alpha3": "COG", "numeric": "178"},
|
|
123
|
+
{"name": "Switzerland", "alpha2": "CH", "alpha3": "CHE", "numeric": "756"},
|
|
124
|
+
{"name": "Côte d'Ivoire", "alpha2": "CI", "alpha3": "CIV", "numeric": "384"},
|
|
125
|
+
{"name": "Cook Islands", "alpha2": "CK", "alpha3": "COK", "numeric": "184"},
|
|
126
|
+
{"name": "Chile", "alpha2": "CL", "alpha3": "CHL", "numeric": "152"},
|
|
127
|
+
{"name": "Cameroon", "alpha2": "CM", "alpha3": "CMR", "numeric": "120"},
|
|
128
|
+
{"name": "China", "alpha2": "CN", "alpha3": "CHN", "numeric": "156"},
|
|
129
|
+
{"name": "Colombia", "alpha2": "CO", "alpha3": "COL", "numeric": "170"},
|
|
130
|
+
{"name": "Costa Rica", "alpha2": "CR", "alpha3": "CRI", "numeric": "188"},
|
|
131
|
+
{"name": "Cuba", "alpha2": "CU", "alpha3": "CUB", "numeric": "192"},
|
|
132
|
+
{"name": "Cabo Verde", "alpha2": "CV", "alpha3": "CPV", "numeric": "132"},
|
|
133
|
+
{"name": "Curaçao", "alpha2": "CW", "alpha3": "CUW", "numeric": "531"},
|
|
134
|
+
{"name": "Christmas Island", "alpha2": "CX", "alpha3": "CXR", "numeric": "162"},
|
|
135
|
+
{"name": "Cyprus", "alpha2": "CY", "alpha3": "CYP", "numeric": "196"},
|
|
136
|
+
{"name": "Czechia", "alpha2": "CZ", "alpha3": "CZE", "numeric": "203"},
|
|
137
|
+
{"name": "Germany", "alpha2": "DE", "alpha3": "DEU", "numeric": "276"},
|
|
138
|
+
{"name": "Djibouti", "alpha2": "DJ", "alpha3": "DJI", "numeric": "262"},
|
|
139
|
+
{"name": "Denmark", "alpha2": "DK", "alpha3": "DNK", "numeric": "208"},
|
|
140
|
+
{"name": "Dominica", "alpha2": "DM", "alpha3": "DMA", "numeric": "212"},
|
|
141
|
+
{"name": "Dominican Republic", "alpha2": "DO", "alpha3": "DOM", "numeric": "214"},
|
|
142
|
+
{"name": "Algeria", "alpha2": "DZ", "alpha3": "DZA", "numeric": "012"},
|
|
143
|
+
{"name": "Ecuador", "alpha2": "EC", "alpha3": "ECU", "numeric": "218"},
|
|
144
|
+
{"name": "Estonia", "alpha2": "EE", "alpha3": "EST", "numeric": "233"},
|
|
145
|
+
{"name": "Egypt", "alpha2": "EG", "alpha3": "EGY", "numeric": "818"},
|
|
146
|
+
{"name": "Western Sahara", "alpha2": "EH", "alpha3": "ESH", "numeric": "732"},
|
|
147
|
+
{"name": "Eritrea", "alpha2": "ER", "alpha3": "ERI", "numeric": "232"},
|
|
148
|
+
{"name": "Spain", "alpha2": "ES", "alpha3": "ESP", "numeric": "724"},
|
|
149
|
+
{"name": "Ethiopia", "alpha2": "ET", "alpha3": "ETH", "numeric": "231"},
|
|
150
|
+
{"name": "Finland", "alpha2": "FI", "alpha3": "FIN", "numeric": "246"},
|
|
151
|
+
{"name": "Fiji", "alpha2": "FJ", "alpha3": "FJI", "numeric": "242"},
|
|
152
|
+
{
|
|
153
|
+
"name": "Falkland Islands (Malvinas)",
|
|
154
|
+
"alpha2": "FK",
|
|
155
|
+
"alpha3": "FLK",
|
|
156
|
+
"numeric": "238",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "Micronesia (Federated States of)",
|
|
160
|
+
"alpha2": "FM",
|
|
161
|
+
"alpha3": "FSM",
|
|
162
|
+
"numeric": "583",
|
|
163
|
+
},
|
|
164
|
+
{"name": "Faroe Islands", "alpha2": "FO", "alpha3": "FRO", "numeric": "234"},
|
|
165
|
+
{"name": "France", "alpha2": "FR", "alpha3": "FRA", "numeric": "250"},
|
|
166
|
+
{"name": "Gabon", "alpha2": "GA", "alpha3": "GAB", "numeric": "266"},
|
|
167
|
+
{
|
|
168
|
+
"name": "United Kingdom of Great Britain and Northern Ireland",
|
|
169
|
+
"alpha2": "GB",
|
|
170
|
+
"alpha3": "GBR",
|
|
171
|
+
"numeric": "826",
|
|
172
|
+
},
|
|
173
|
+
{"name": "Grenada", "alpha2": "GD", "alpha3": "GRD", "numeric": "308"},
|
|
174
|
+
{"name": "Georgia", "alpha2": "GE", "alpha3": "GEO", "numeric": "268"},
|
|
175
|
+
{"name": "French Guiana", "alpha2": "GF", "alpha3": "GUF", "numeric": "254"},
|
|
176
|
+
{"name": "Guernsey", "alpha2": "GG", "alpha3": "GGY", "numeric": "831"},
|
|
177
|
+
{"name": "Ghana", "alpha2": "GH", "alpha3": "GHA", "numeric": "288"},
|
|
178
|
+
{"name": "Gibraltar", "alpha2": "GI", "alpha3": "GIB", "numeric": "292"},
|
|
179
|
+
{"name": "Greenland", "alpha2": "GL", "alpha3": "GRL", "numeric": "304"},
|
|
180
|
+
{"name": "Gambia", "alpha2": "GM", "alpha3": "GMB", "numeric": "270"},
|
|
181
|
+
{"name": "Guinea", "alpha2": "GN", "alpha3": "GIN", "numeric": "324"},
|
|
182
|
+
{"name": "Guadeloupe", "alpha2": "GP", "alpha3": "GLP", "numeric": "312"},
|
|
183
|
+
{"name": "Equatorial Guinea", "alpha2": "GQ", "alpha3": "GNQ", "numeric": "226"},
|
|
184
|
+
{"name": "Greece", "alpha2": "GR", "alpha3": "GRC", "numeric": "300"},
|
|
185
|
+
{
|
|
186
|
+
"name": "South Georgia and the South Sandwich Islands",
|
|
187
|
+
"alpha2": "GS",
|
|
188
|
+
"alpha3": "SGS",
|
|
189
|
+
"numeric": "239",
|
|
190
|
+
},
|
|
191
|
+
{"name": "Guatemala", "alpha2": "GT", "alpha3": "GTM", "numeric": "320"},
|
|
192
|
+
{"name": "Guam", "alpha2": "GU", "alpha3": "GUM", "numeric": "316"},
|
|
193
|
+
{"name": "Guinea-Bissau", "alpha2": "GW", "alpha3": "GNB", "numeric": "624"},
|
|
194
|
+
{"name": "Guyana", "alpha2": "GY", "alpha3": "GUY", "numeric": "328"},
|
|
195
|
+
{"name": "Hong Kong", "alpha2": "HK", "alpha3": "HKG", "numeric": "344"},
|
|
196
|
+
{
|
|
197
|
+
"name": "Heard Island and McDonald Islands",
|
|
198
|
+
"alpha2": "HM",
|
|
199
|
+
"alpha3": "HMD",
|
|
200
|
+
"numeric": "334",
|
|
201
|
+
},
|
|
202
|
+
{"name": "Honduras", "alpha2": "HN", "alpha3": "HND", "numeric": "340"},
|
|
203
|
+
{"name": "Croatia", "alpha2": "HR", "alpha3": "HRV", "numeric": "191"},
|
|
204
|
+
{"name": "Haiti", "alpha2": "HT", "alpha3": "HTI", "numeric": "332"},
|
|
205
|
+
{"name": "Hungary", "alpha2": "HU", "alpha3": "HUN", "numeric": "348"},
|
|
206
|
+
{"name": "Indonesia", "alpha2": "ID", "alpha3": "IDN", "numeric": "360"},
|
|
207
|
+
{"name": "Ireland", "alpha2": "IE", "alpha3": "IRL", "numeric": "372"},
|
|
208
|
+
{"name": "Israel", "alpha2": "IL", "alpha3": "ISR", "numeric": "376"},
|
|
209
|
+
{"name": "Isle of Man", "alpha2": "IM", "alpha3": "IMN", "numeric": "833"},
|
|
210
|
+
{"name": "India", "alpha2": "IN", "alpha3": "IND", "numeric": "356"},
|
|
211
|
+
{
|
|
212
|
+
"name": "British Indian Ocean Territory",
|
|
213
|
+
"alpha2": "IO",
|
|
214
|
+
"alpha3": "IOT",
|
|
215
|
+
"numeric": "086",
|
|
216
|
+
},
|
|
217
|
+
{"name": "Iraq", "alpha2": "IQ", "alpha3": "IRQ", "numeric": "368"},
|
|
218
|
+
{
|
|
219
|
+
"name": "Iran (Islamic Republic of)",
|
|
220
|
+
"alpha2": "IR",
|
|
221
|
+
"alpha3": "IRN",
|
|
222
|
+
"numeric": "364",
|
|
223
|
+
},
|
|
224
|
+
{"name": "Iceland", "alpha2": "IS", "alpha3": "ISL", "numeric": "352"},
|
|
225
|
+
{"name": "Italy", "alpha2": "IT", "alpha3": "ITA", "numeric": "380"},
|
|
226
|
+
{"name": "Jersey", "alpha2": "JE", "alpha3": "JEY", "numeric": "832"},
|
|
227
|
+
{"name": "Jamaica", "alpha2": "JM", "alpha3": "JAM", "numeric": "388"},
|
|
228
|
+
{"name": "Jordan", "alpha2": "JO", "alpha3": "JOR", "numeric": "400"},
|
|
229
|
+
{"name": "Japan", "alpha2": "JP", "alpha3": "JPN", "numeric": "392"},
|
|
230
|
+
{"name": "Kenya", "alpha2": "KE", "alpha3": "KEN", "numeric": "404"},
|
|
231
|
+
{"name": "Kyrgyzstan", "alpha2": "KG", "alpha3": "KGZ", "numeric": "417"},
|
|
232
|
+
{"name": "Cambodia", "alpha2": "KH", "alpha3": "KHM", "numeric": "116"},
|
|
233
|
+
{"name": "Kiribati", "alpha2": "KI", "alpha3": "KIR", "numeric": "296"},
|
|
234
|
+
{"name": "Comoros", "alpha2": "KM", "alpha3": "COM", "numeric": "174"},
|
|
235
|
+
{
|
|
236
|
+
"name": "Saint Kitts and Nevis",
|
|
237
|
+
"alpha2": "KN",
|
|
238
|
+
"alpha3": "KNA",
|
|
239
|
+
"numeric": "659",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"name": "Korea (Democratic People's Republic of)",
|
|
243
|
+
"alpha2": "KP",
|
|
244
|
+
"alpha3": "PRK",
|
|
245
|
+
"numeric": "408",
|
|
246
|
+
},
|
|
247
|
+
{"name": "Korea, Republic of", "alpha2": "KR", "alpha3": "KOR", "numeric": "410"},
|
|
248
|
+
{"name": "Kuwait", "alpha2": "KW", "alpha3": "KWT", "numeric": "414"},
|
|
249
|
+
{"name": "Cayman Islands", "alpha2": "KY", "alpha3": "CYM", "numeric": "136"},
|
|
250
|
+
{"name": "Kazakhstan", "alpha2": "KZ", "alpha3": "KAZ", "numeric": "398"},
|
|
251
|
+
{
|
|
252
|
+
"name": "Lao People's Democratic Republic",
|
|
253
|
+
"alpha2": "LA",
|
|
254
|
+
"alpha3": "LAO",
|
|
255
|
+
"numeric": "418",
|
|
256
|
+
},
|
|
257
|
+
{"name": "Lebanon", "alpha2": "LB", "alpha3": "LBN", "numeric": "422"},
|
|
258
|
+
{"name": "Saint Lucia", "alpha2": "LC", "alpha3": "LCA", "numeric": "662"},
|
|
259
|
+
{"name": "Liechtenstein", "alpha2": "LI", "alpha3": "LIE", "numeric": "438"},
|
|
260
|
+
{"name": "Sri Lanka", "alpha2": "LK", "alpha3": "LKA", "numeric": "144"},
|
|
261
|
+
{"name": "Liberia", "alpha2": "LR", "alpha3": "LBR", "numeric": "430"},
|
|
262
|
+
{"name": "Lesotho", "alpha2": "LS", "alpha3": "LSO", "numeric": "426"},
|
|
263
|
+
{"name": "Lithuania", "alpha2": "LT", "alpha3": "LTU", "numeric": "440"},
|
|
264
|
+
{"name": "Luxembourg", "alpha2": "LU", "alpha3": "LUX", "numeric": "442"},
|
|
265
|
+
{"name": "Latvia", "alpha2": "LV", "alpha3": "LVA", "numeric": "428"},
|
|
266
|
+
{"name": "Libya", "alpha2": "LY", "alpha3": "LBY", "numeric": "434"},
|
|
267
|
+
{"name": "Morocco", "alpha2": "MA", "alpha3": "MAR", "numeric": "504"},
|
|
268
|
+
{"name": "Monaco", "alpha2": "MC", "alpha3": "MCO", "numeric": "492"},
|
|
269
|
+
{"name": "Moldova, Republic of", "alpha2": "MD", "alpha3": "MDA", "numeric": "498"},
|
|
270
|
+
{"name": "Montenegro", "alpha2": "ME", "alpha3": "MNE", "numeric": "499"},
|
|
271
|
+
{
|
|
272
|
+
"name": "Saint Martin (French part)",
|
|
273
|
+
"alpha2": "MF",
|
|
274
|
+
"alpha3": "MAF",
|
|
275
|
+
"numeric": "663",
|
|
276
|
+
},
|
|
277
|
+
{"name": "Madagascar", "alpha2": "MG", "alpha3": "MDG", "numeric": "450"},
|
|
278
|
+
{"name": "Marshall Islands", "alpha2": "MH", "alpha3": "MHL", "numeric": "584"},
|
|
279
|
+
{"name": "North Macedonia", "alpha2": "MK", "alpha3": "MKD", "numeric": "807"},
|
|
280
|
+
{"name": "Mali", "alpha2": "ML", "alpha3": "MLI", "numeric": "466"},
|
|
281
|
+
{"name": "Myanmar", "alpha2": "MM", "alpha3": "MMR", "numeric": "104"},
|
|
282
|
+
{"name": "Mongolia", "alpha2": "MN", "alpha3": "MNG", "numeric": "496"},
|
|
283
|
+
{"name": "Macao", "alpha2": "MO", "alpha3": "MAC", "numeric": "446"},
|
|
284
|
+
{
|
|
285
|
+
"name": "Northern Mariana Islands",
|
|
286
|
+
"alpha2": "MP",
|
|
287
|
+
"alpha3": "MNP",
|
|
288
|
+
"numeric": "580",
|
|
289
|
+
},
|
|
290
|
+
{"name": "Martinique", "alpha2": "MQ", "alpha3": "MTQ", "numeric": "474"},
|
|
291
|
+
{"name": "Mauritania", "alpha2": "MR", "alpha3": "MRT", "numeric": "478"},
|
|
292
|
+
{"name": "Montserrat", "alpha2": "MS", "alpha3": "MSR", "numeric": "500"},
|
|
293
|
+
{"name": "Malta", "alpha2": "MT", "alpha3": "MLT", "numeric": "470"},
|
|
294
|
+
{"name": "Mauritius", "alpha2": "MU", "alpha3": "MUS", "numeric": "480"},
|
|
295
|
+
{"name": "Maldives", "alpha2": "MV", "alpha3": "MDV", "numeric": "462"},
|
|
296
|
+
{"name": "Malawi", "alpha2": "MW", "alpha3": "MWI", "numeric": "454"},
|
|
297
|
+
{"name": "Mexico", "alpha2": "MX", "alpha3": "MEX", "numeric": "484"},
|
|
298
|
+
{"name": "Malaysia", "alpha2": "MY", "alpha3": "MYS", "numeric": "458"},
|
|
299
|
+
{"name": "Mozambique", "alpha2": "MZ", "alpha3": "MOZ", "numeric": "508"},
|
|
300
|
+
{"name": "Namibia", "alpha2": "NA", "alpha3": "NAM", "numeric": "516"},
|
|
301
|
+
{"name": "New Caledonia", "alpha2": "NC", "alpha3": "NCL", "numeric": "540"},
|
|
302
|
+
{"name": "Niger", "alpha2": "NE", "alpha3": "NER", "numeric": "562"},
|
|
303
|
+
{"name": "Norfolk Island", "alpha2": "NF", "alpha3": "NFK", "numeric": "574"},
|
|
304
|
+
{"name": "Nigeria", "alpha2": "NG", "alpha3": "NGA", "numeric": "566"},
|
|
305
|
+
{"name": "Nicaragua", "alpha2": "NI", "alpha3": "NIC", "numeric": "558"},
|
|
306
|
+
{
|
|
307
|
+
"name": "Netherlands, Kingdom of the",
|
|
308
|
+
"alpha2": "NL",
|
|
309
|
+
"alpha3": "NLD",
|
|
310
|
+
"numeric": "528",
|
|
311
|
+
},
|
|
312
|
+
{"name": "Norway", "alpha2": "NO", "alpha3": "NOR", "numeric": "578"},
|
|
313
|
+
{"name": "Nepal", "alpha2": "NP", "alpha3": "NPL", "numeric": "524"},
|
|
314
|
+
{"name": "Nauru", "alpha2": "NR", "alpha3": "NRU", "numeric": "520"},
|
|
315
|
+
{"name": "Niue", "alpha2": "NU", "alpha3": "NIU", "numeric": "570"},
|
|
316
|
+
{"name": "New Zealand", "alpha2": "NZ", "alpha3": "NZL", "numeric": "554"},
|
|
317
|
+
{"name": "Oman", "alpha2": "OM", "alpha3": "OMN", "numeric": "512"},
|
|
318
|
+
{"name": "Panama", "alpha2": "PA", "alpha3": "PAN", "numeric": "591"},
|
|
319
|
+
{"name": "Peru", "alpha2": "PE", "alpha3": "PER", "numeric": "604"},
|
|
320
|
+
{"name": "French Polynesia", "alpha2": "PF", "alpha3": "PYF", "numeric": "258"},
|
|
321
|
+
{"name": "Papua New Guinea", "alpha2": "PG", "alpha3": "PNG", "numeric": "598"},
|
|
322
|
+
{"name": "Philippines", "alpha2": "PH", "alpha3": "PHL", "numeric": "608"},
|
|
323
|
+
{"name": "Pakistan", "alpha2": "PK", "alpha3": "PAK", "numeric": "586"},
|
|
324
|
+
{"name": "Poland", "alpha2": "PL", "alpha3": "POL", "numeric": "616"},
|
|
325
|
+
{
|
|
326
|
+
"name": "Saint Pierre and Miquelon",
|
|
327
|
+
"alpha2": "PM",
|
|
328
|
+
"alpha3": "SPM",
|
|
329
|
+
"numeric": "666",
|
|
330
|
+
},
|
|
331
|
+
{"name": "Pitcairn", "alpha2": "PN", "alpha3": "PCN", "numeric": "612"},
|
|
332
|
+
{"name": "Puerto Rico", "alpha2": "PR", "alpha3": "PRI", "numeric": "630"},
|
|
333
|
+
{"name": "Palestine, State of", "alpha2": "PS", "alpha3": "PSE", "numeric": "275"},
|
|
334
|
+
{"name": "Portugal", "alpha2": "PT", "alpha3": "PRT", "numeric": "620"},
|
|
335
|
+
{"name": "Palau", "alpha2": "PW", "alpha3": "PLW", "numeric": "585"},
|
|
336
|
+
{"name": "Paraguay", "alpha2": "PY", "alpha3": "PRY", "numeric": "600"},
|
|
337
|
+
{"name": "Qatar", "alpha2": "QA", "alpha3": "QAT", "numeric": "634"},
|
|
338
|
+
{"name": "Réunion", "alpha2": "RE", "alpha3": "REU", "numeric": "638"},
|
|
339
|
+
{"name": "Romania", "alpha2": "RO", "alpha3": "ROU", "numeric": "642"},
|
|
340
|
+
{"name": "Serbia", "alpha2": "RS", "alpha3": "SRB", "numeric": "688"},
|
|
341
|
+
{"name": "Russian Federation", "alpha2": "RU", "alpha3": "RUS", "numeric": "643"},
|
|
342
|
+
{"name": "Rwanda", "alpha2": "RW", "alpha3": "RWA", "numeric": "646"},
|
|
343
|
+
{"name": "Saudi Arabia", "alpha2": "SA", "alpha3": "SAU", "numeric": "682"},
|
|
344
|
+
{"name": "Solomon Islands", "alpha2": "SB", "alpha3": "SLB", "numeric": "090"},
|
|
345
|
+
{"name": "Seychelles", "alpha2": "SC", "alpha3": "SYC", "numeric": "690"},
|
|
346
|
+
{"name": "Sudan", "alpha2": "SD", "alpha3": "SDN", "numeric": "729"},
|
|
347
|
+
{"name": "Sweden", "alpha2": "SE", "alpha3": "SWE", "numeric": "752"},
|
|
348
|
+
{"name": "Singapore", "alpha2": "SG", "alpha3": "SGP", "numeric": "702"},
|
|
349
|
+
{
|
|
350
|
+
"name": "Saint Helena, Ascension and Tristan da Cunha",
|
|
351
|
+
"alpha2": "SH",
|
|
352
|
+
"alpha3": "SHN",
|
|
353
|
+
"numeric": "654",
|
|
354
|
+
},
|
|
355
|
+
{"name": "Slovenia", "alpha2": "SI", "alpha3": "SVN", "numeric": "705"},
|
|
356
|
+
{
|
|
357
|
+
"name": "Svalbard and Jan Mayen",
|
|
358
|
+
"alpha2": "SJ",
|
|
359
|
+
"alpha3": "SJM",
|
|
360
|
+
"numeric": "744",
|
|
361
|
+
},
|
|
362
|
+
{"name": "Slovakia", "alpha2": "SK", "alpha3": "SVK", "numeric": "703"},
|
|
363
|
+
{"name": "Sierra Leone", "alpha2": "SL", "alpha3": "SLE", "numeric": "694"},
|
|
364
|
+
{"name": "San Marino", "alpha2": "SM", "alpha3": "SMR", "numeric": "674"},
|
|
365
|
+
{"name": "Senegal", "alpha2": "SN", "alpha3": "SEN", "numeric": "686"},
|
|
366
|
+
{"name": "Somalia", "alpha2": "SO", "alpha3": "SOM", "numeric": "706"},
|
|
367
|
+
{"name": "Suriname", "alpha2": "SR", "alpha3": "SUR", "numeric": "740"},
|
|
368
|
+
{"name": "South Sudan", "alpha2": "SS", "alpha3": "SSD", "numeric": "728"},
|
|
369
|
+
{
|
|
370
|
+
"name": "Sao Tome and Principe",
|
|
371
|
+
"alpha2": "ST",
|
|
372
|
+
"alpha3": "STP",
|
|
373
|
+
"numeric": "678",
|
|
374
|
+
},
|
|
375
|
+
{"name": "El Salvador", "alpha2": "SV", "alpha3": "SLV", "numeric": "222"},
|
|
376
|
+
{
|
|
377
|
+
"name": "Sint Maarten (Dutch part)",
|
|
378
|
+
"alpha2": "SX",
|
|
379
|
+
"alpha3": "SXM",
|
|
380
|
+
"numeric": "534",
|
|
381
|
+
},
|
|
382
|
+
{"name": "Syrian Arab Republic", "alpha2": "SY", "alpha3": "SYR", "numeric": "760"},
|
|
383
|
+
{"name": "Eswatini", "alpha2": "SZ", "alpha3": "SWZ", "numeric": "748"},
|
|
384
|
+
{
|
|
385
|
+
"name": "Turks and Caicos Islands",
|
|
386
|
+
"alpha2": "TC",
|
|
387
|
+
"alpha3": "TCA",
|
|
388
|
+
"numeric": "796",
|
|
389
|
+
},
|
|
390
|
+
{"name": "Chad", "alpha2": "TD", "alpha3": "TCD", "numeric": "148"},
|
|
391
|
+
{
|
|
392
|
+
"name": "French Southern Territories",
|
|
393
|
+
"alpha2": "TF",
|
|
394
|
+
"alpha3": "ATF",
|
|
395
|
+
"numeric": "260",
|
|
396
|
+
},
|
|
397
|
+
{"name": "Togo", "alpha2": "TG", "alpha3": "TGO", "numeric": "768"},
|
|
398
|
+
{"name": "Thailand", "alpha2": "TH", "alpha3": "THA", "numeric": "764"},
|
|
399
|
+
{"name": "Tajikistan", "alpha2": "TJ", "alpha3": "TJK", "numeric": "762"},
|
|
400
|
+
{"name": "Tokelau", "alpha2": "TK", "alpha3": "TKL", "numeric": "772"},
|
|
401
|
+
{"name": "Timor-Leste", "alpha2": "TL", "alpha3": "TLS", "numeric": "626"},
|
|
402
|
+
{"name": "Turkmenistan", "alpha2": "TM", "alpha3": "TKM", "numeric": "795"},
|
|
403
|
+
{"name": "Tunisia", "alpha2": "TN", "alpha3": "TUN", "numeric": "788"},
|
|
404
|
+
{"name": "Tonga", "alpha2": "TO", "alpha3": "TON", "numeric": "776"},
|
|
405
|
+
{"name": "Türkiye", "alpha2": "TR", "alpha3": "TUR", "numeric": "792"},
|
|
406
|
+
{"name": "Trinidad and Tobago", "alpha2": "TT", "alpha3": "TTO", "numeric": "780"},
|
|
407
|
+
{"name": "Tuvalu", "alpha2": "TV", "alpha3": "TUV", "numeric": "798"},
|
|
408
|
+
{
|
|
409
|
+
"name": "Taiwan, Province of China",
|
|
410
|
+
"alpha2": "TW",
|
|
411
|
+
"alpha3": "TWN",
|
|
412
|
+
"numeric": "158",
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"name": "Tanzania, United Republic of",
|
|
416
|
+
"alpha2": "TZ",
|
|
417
|
+
"alpha3": "TZA",
|
|
418
|
+
"numeric": "834",
|
|
419
|
+
},
|
|
420
|
+
{"name": "Ukraine", "alpha2": "UA", "alpha3": "UKR", "numeric": "804"},
|
|
421
|
+
{"name": "Uganda", "alpha2": "UG", "alpha3": "UGA", "numeric": "800"},
|
|
422
|
+
{
|
|
423
|
+
"name": "United States Minor Outlying Islands",
|
|
424
|
+
"alpha2": "UM",
|
|
425
|
+
"alpha3": "UMI",
|
|
426
|
+
"numeric": "581",
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"name": "United States of America",
|
|
430
|
+
"alpha2": "US",
|
|
431
|
+
"alpha3": "USA",
|
|
432
|
+
"numeric": "840",
|
|
433
|
+
},
|
|
434
|
+
{"name": "Uruguay", "alpha2": "UY", "alpha3": "URY", "numeric": "858"},
|
|
435
|
+
{"name": "Uzbekistan", "alpha2": "UZ", "alpha3": "UZB", "numeric": "860"},
|
|
436
|
+
{"name": "Holy See", "alpha2": "VA", "alpha3": "VAT", "numeric": "336"},
|
|
437
|
+
{
|
|
438
|
+
"name": "Saint Vincent and the Grenadines",
|
|
439
|
+
"alpha2": "VC",
|
|
440
|
+
"alpha3": "VCT",
|
|
441
|
+
"numeric": "670",
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"name": "Venezuela (Bolivarian Republic of)",
|
|
445
|
+
"alpha2": "VE",
|
|
446
|
+
"alpha3": "VEN",
|
|
447
|
+
"numeric": "862",
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
"name": "Virgin Islands (British)",
|
|
451
|
+
"alpha2": "VG",
|
|
452
|
+
"alpha3": "VGB",
|
|
453
|
+
"numeric": "092",
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"name": "Virgin Islands (U.S.)",
|
|
457
|
+
"alpha2": "VI",
|
|
458
|
+
"alpha3": "VIR",
|
|
459
|
+
"numeric": "850",
|
|
460
|
+
},
|
|
461
|
+
{"name": "Viet Nam", "alpha2": "VN", "alpha3": "VNM", "numeric": "704"},
|
|
462
|
+
{"name": "Vanuatu", "alpha2": "VU", "alpha3": "VUT", "numeric": "548"},
|
|
463
|
+
{"name": "Wallis and Futuna", "alpha2": "WF", "alpha3": "WLF", "numeric": "876"},
|
|
464
|
+
{"name": "Samoa", "alpha2": "WS", "alpha3": "WSM", "numeric": "882"},
|
|
465
|
+
{"name": "Yemen", "alpha2": "YE", "alpha3": "YEM", "numeric": "887"},
|
|
466
|
+
{"name": "Mayotte", "alpha2": "YT", "alpha3": "MYT", "numeric": "175"},
|
|
467
|
+
{"name": "South Africa", "alpha2": "ZA", "alpha3": "ZAF", "numeric": "710"},
|
|
468
|
+
{"name": "Zambia", "alpha2": "ZM", "alpha3": "ZMB", "numeric": "894"},
|
|
469
|
+
{"name": "Zimbabwe", "alpha2": "ZW", "alpha3": "ZWE", "numeric": "716"},
|
|
470
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class String:
|
|
5
|
+
@classmethod
|
|
6
|
+
def stringContains(cls, str_to_test: str, val: str):
|
|
7
|
+
"""Tests if a string contains another string"""
|
|
8
|
+
res = (
|
|
9
|
+
isinstance(str_to_test, str) and isinstance(val, str) and val in str_to_test
|
|
10
|
+
)
|
|
11
|
+
return res
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def startsWith(self, str_to_test: str, val: str):
|
|
15
|
+
"""Tests if a string starts with another string"""
|
|
16
|
+
return (
|
|
17
|
+
isinstance(str_to_test, str)
|
|
18
|
+
and isinstance(val, str)
|
|
19
|
+
and str_to_test.startswith(val)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def toLowerCase(self, val: str):
|
|
24
|
+
"""Casts to lower case"""
|
|
25
|
+
if isinstance(val, str):
|
|
26
|
+
return val.lower()
|
|
27
|
+
|
|
28
|
+
return val
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def toUpperCase(self, val: str):
|
|
32
|
+
"""Casts to upper case"""
|
|
33
|
+
if isinstance(val, str):
|
|
34
|
+
return val.upper()
|
|
35
|
+
return val
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def append(self, val1: str, val2: str):
|
|
39
|
+
"""Concatenates two strings"""
|
|
40
|
+
return f"{val1}${val2}"
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def join(self, separator: str, *vals: Tuple[str]):
|
|
44
|
+
"""Returns a joined string using separator"""
|
|
45
|
+
return separator.join(list(vals))
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def removeSpaces(self, val: str):
|
|
49
|
+
"""Removes spaces from a string"""
|
|
50
|
+
return str(val).replace(" ", "")
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def replace(self, val: str, match: str, replacement: str):
|
|
54
|
+
"""Replace all occurances of match with replacement in val"""
|
|
55
|
+
return str(val).replace(match, replacement)
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def replaceFirst(self, val: str, match: str, replacement: str):
|
|
59
|
+
"""Replace first occurance of match with replacement in val"""
|
|
60
|
+
return str(val).replace(match, replacement, 1)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def startsWith(self, val: str, test: str):
|
|
64
|
+
"""Returns whether val starts with test"""
|
|
65
|
+
return str(val).startswith(test)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def contains(self, val: str, test: str):
|
|
69
|
+
"""Return whether val contains test"""
|
|
70
|
+
return str(test) in str(val)
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def stringSwitch(self, val: str, default: str, *kv_pairs: Tuple[str]):
|
|
74
|
+
"""Set defaultss"""
|
|
75
|
+
if len(kv_pairs) % 2 != 0:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
kv_pairs = list(kv_pairs)
|
|
79
|
+
|
|
80
|
+
while kv_pairs:
|
|
81
|
+
k = kv_pairs.pop(0)
|
|
82
|
+
v = kv_pairs.pop(0)
|
|
83
|
+
|
|
84
|
+
if k in val:
|
|
85
|
+
return v
|
|
86
|
+
|
|
87
|
+
return default
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def substring(val: str, start: int, end: int):
|
|
91
|
+
"""Return a substring of val starting at index start"""
|
|
92
|
+
return val[int(start) : int(end)]
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def substringAfter(val: str, search: str):
|
|
96
|
+
try:
|
|
97
|
+
start = str(val).index(str(search))
|
|
98
|
+
except ValueError:
|
|
99
|
+
return val
|
|
100
|
+
|
|
101
|
+
return val[start:]
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def substringBefore(val: str, search: str):
|
|
105
|
+
"""Return substring of val starting after the index of search string"""
|
|
106
|
+
try:
|
|
107
|
+
end = str(val).index(str(search))
|
|
108
|
+
except ValueError:
|
|
109
|
+
return val
|
|
110
|
+
|
|
111
|
+
return val[0:end]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# pyright: reportUndefinedVariable=false
|
|
2
|
+
from sly import Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SyntaxError(Exception):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExpressionLexer(Lexer):
|
|
10
|
+
# reflags = re.IGNORECASE
|
|
11
|
+
# Set of token names.
|
|
12
|
+
tokens = {
|
|
13
|
+
STRING,
|
|
14
|
+
INT,
|
|
15
|
+
NAME,
|
|
16
|
+
NE,
|
|
17
|
+
GTE,
|
|
18
|
+
LTE,
|
|
19
|
+
GT,
|
|
20
|
+
LT,
|
|
21
|
+
EQ,
|
|
22
|
+
AND,
|
|
23
|
+
OR,
|
|
24
|
+
NOT,
|
|
25
|
+
NULL,
|
|
26
|
+
BOOL,
|
|
27
|
+
MEMBEROF,
|
|
28
|
+
MEMBEROFANY,
|
|
29
|
+
USER,
|
|
30
|
+
CLASS,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Set of literal characters
|
|
34
|
+
literals = {"(", ")", ",", ".", "{", "}"}
|
|
35
|
+
|
|
36
|
+
# String containing ignored characters
|
|
37
|
+
ignore = " \t"
|
|
38
|
+
|
|
39
|
+
AND = r"and|And|AND"
|
|
40
|
+
NE = r"(!=|ne|NE|Ne)"
|
|
41
|
+
NOT = r"!|not|Not|NOT"
|
|
42
|
+
GTE = ">="
|
|
43
|
+
GT = ">"
|
|
44
|
+
LT = "<"
|
|
45
|
+
LTE = "<="
|
|
46
|
+
OR = r"or|OR|Or"
|
|
47
|
+
EQ = r"(==|eq|EQ|Eq)"
|
|
48
|
+
BOOL = r"(true|false|True|False|TRUE|FALSE)"
|
|
49
|
+
MEMBEROFANY = "isMemberOfAnyGroup"
|
|
50
|
+
MEMBEROF = "isMemberOfGroup"
|
|
51
|
+
USER = r"user\b"
|
|
52
|
+
|
|
53
|
+
NULL = r"null|NULL|Null"
|
|
54
|
+
CLASS = r"String|Arrays|Convert|Iso3166Convert|Groups"
|
|
55
|
+
INT = r"\d+"
|
|
56
|
+
STRING = r""""([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'|''|\"\""""
|
|
57
|
+
NAME = r"[a-zA-Z_][a-zA-Z0-9\-_]*"
|
|
58
|
+
|
|
59
|
+
# Line number tracking
|
|
60
|
+
@_(r"\n+")
|
|
61
|
+
def ignore_newline(self, t):
|
|
62
|
+
self.lineno += t.value.count("\n")
|
|
63
|
+
|
|
64
|
+
def error(self, t):
|
|
65
|
+
msg = (
|
|
66
|
+
f"Bad character '{t.value[0]}' at line {self.lineno} character {self.index}"
|
|
67
|
+
)
|
|
68
|
+
raise SyntaxError(msg)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# pyright: reportUndefinedVariable=false
|
|
2
|
+
import logging
|
|
3
|
+
from os import environ
|
|
4
|
+
from sys import stdout
|
|
5
|
+
from typing import Any, Dict, List, Sequence
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
|
|
8
|
+
import sly
|
|
9
|
+
from sly.yacc import YaccProduction
|
|
10
|
+
|
|
11
|
+
from . import expression_classes as _default_expression_classes
|
|
12
|
+
from .expression_classes.arrays import _ArrayType
|
|
13
|
+
from .lexer import ExpressionLexer
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExpressionParser(sly.Parser):
|
|
17
|
+
if debug_file := environ.get("OKTA_EXPRESSION_PARSER_LOG_FILE"):
|
|
18
|
+
#: Writes grammar rules to a file for debugging
|
|
19
|
+
debugfile: str = environ.get(debug_file)
|
|
20
|
+
|
|
21
|
+
#: Gets the token list from the lexer (required)
|
|
22
|
+
tokens: Sequence = ExpressionLexer.tokens
|
|
23
|
+
|
|
24
|
+
#: Defines precendence of operators
|
|
25
|
+
precedence = (
|
|
26
|
+
("left", OR), # noqa: 821
|
|
27
|
+
("left", AND), # noqa: 821
|
|
28
|
+
("right", NOT), # noqa: 821
|
|
29
|
+
("nonassoc", EQ, NE), # noqa: 821
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
group_ids: List[str] = [],
|
|
35
|
+
user_profile: Dict[str, Any] = {},
|
|
36
|
+
log_to_stdout: bool = False,
|
|
37
|
+
log_level: str = "INFO",
|
|
38
|
+
expression_classes: ModuleType = _default_expression_classes,
|
|
39
|
+
group_data: dict = {},
|
|
40
|
+
) -> None:
|
|
41
|
+
#: A module containing classes used in expressions, such as `Arrays` or `String`
|
|
42
|
+
self._expression_classes = expression_classes
|
|
43
|
+
|
|
44
|
+
# Required for doing translations between group ID's and other group attributes.
|
|
45
|
+
# Without passing group_data into the parser all `Group` methods will return empty
|
|
46
|
+
if (
|
|
47
|
+
group_data
|
|
48
|
+
and hasattr(self._expression_classes, "Groups")
|
|
49
|
+
and type(self._expression_classes.Groups) == type
|
|
50
|
+
):
|
|
51
|
+
self._expression_classes.Groups.group_data = group_data
|
|
52
|
+
|
|
53
|
+
self.group_data = group_data
|
|
54
|
+
|
|
55
|
+
self.__group_ids: str = group_ids
|
|
56
|
+
self.__user_profile: str = user_profile
|
|
57
|
+
self.logger: logging.Logger = logging.getLogger()
|
|
58
|
+
|
|
59
|
+
self.logger.setLevel(log_level)
|
|
60
|
+
|
|
61
|
+
if log_to_stdout:
|
|
62
|
+
self.logger.addHandler(logging.StreamHandler(stream=stdout))
|
|
63
|
+
|
|
64
|
+
def parse(self, expression: str):
|
|
65
|
+
return super().parse(ExpressionLexer().tokenize(expression))
|
|
66
|
+
|
|
67
|
+
@_("operand EQ operand") # noqa: 821
|
|
68
|
+
def condition(self, p: YaccProduction) -> bool:
|
|
69
|
+
operand0 = p.operand0
|
|
70
|
+
operand1 = p.operand1
|
|
71
|
+
return operand0 == operand1
|
|
72
|
+
|
|
73
|
+
@_("path EQ operand") # noqa: 821
|
|
74
|
+
def condition(self, p: YaccProduction) -> bool:
|
|
75
|
+
path = p.path
|
|
76
|
+
value = p.operand
|
|
77
|
+
|
|
78
|
+
return path == value
|
|
79
|
+
|
|
80
|
+
@_("operand NE operand") # noqa: 821
|
|
81
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
82
|
+
operand0 = p.operand0
|
|
83
|
+
operand1 = p.operand1
|
|
84
|
+
return operand0 != operand1
|
|
85
|
+
|
|
86
|
+
@_("operand GTE operand") # noqa: 821
|
|
87
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
88
|
+
operand0 = p.operand0
|
|
89
|
+
operand1 = p.operand1
|
|
90
|
+
return type(operand0) == type(operand1) and operand0 >= operand1
|
|
91
|
+
|
|
92
|
+
@_("operand LTE operand") # noqa: 821
|
|
93
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
94
|
+
operand0 = p.operand0
|
|
95
|
+
operand1 = p.operand1
|
|
96
|
+
return type(operand0) == type(operand1) and operand0 <= operand1
|
|
97
|
+
|
|
98
|
+
@_("operand LT operand") # noqa: 821
|
|
99
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
100
|
+
operand0 = p.operand0
|
|
101
|
+
operand1 = p.operand1
|
|
102
|
+
return type(operand0) == type(operand1) and operand0 < operand1
|
|
103
|
+
|
|
104
|
+
@_("operand GT operand") # noqa: 821
|
|
105
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
106
|
+
operand0 = p.operand0
|
|
107
|
+
operand1 = p.operand1
|
|
108
|
+
return type(operand0) == type(operand1) and operand0 > operand1
|
|
109
|
+
|
|
110
|
+
@_("condition AND condition") # noqa: 821
|
|
111
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
112
|
+
condition0 = p.condition0
|
|
113
|
+
condition1 = p.condition1
|
|
114
|
+
return condition0 and condition1
|
|
115
|
+
|
|
116
|
+
@_("condition OR condition") # noqa: 821
|
|
117
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
118
|
+
condition0 = p.condition0
|
|
119
|
+
condition1 = p.condition1
|
|
120
|
+
return condition0 or condition1
|
|
121
|
+
|
|
122
|
+
@_("NOT condition") # noqa: 821
|
|
123
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
124
|
+
condition = p.condition
|
|
125
|
+
return not condition
|
|
126
|
+
|
|
127
|
+
@_("NOT operand") # noqa: 821
|
|
128
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
129
|
+
return not p.operand
|
|
130
|
+
|
|
131
|
+
@_('"(" condition ")"') # noqa: 821
|
|
132
|
+
def condition(self, p: YaccProduction) -> bool: # noqa: 811
|
|
133
|
+
condition = p.condition
|
|
134
|
+
return condition
|
|
135
|
+
|
|
136
|
+
@_('operand "," operand') # noqa: 821
|
|
137
|
+
def operand(self, p: YaccProduction) -> List[Any]: # noqa: 811
|
|
138
|
+
operand0 = p.operand0
|
|
139
|
+
operand1 = p.operand1
|
|
140
|
+
|
|
141
|
+
# if not isinstance(operand0, tuple):
|
|
142
|
+
# return (operand0, operand1)
|
|
143
|
+
#
|
|
144
|
+
# else:
|
|
145
|
+
# return operand0 + (operand1,)
|
|
146
|
+
|
|
147
|
+
if isinstance(operand0, tuple) and isinstance(operand1, tuple):
|
|
148
|
+
res = operand0 + operand1
|
|
149
|
+
elif isinstance(operand0, tuple) and not isinstance(operand1, tuple):
|
|
150
|
+
res = operand0 + (operand1,)
|
|
151
|
+
elif not isinstance(operand0, tuple) and isinstance(operand1, tuple):
|
|
152
|
+
res = (operand0,) + operand1
|
|
153
|
+
else:
|
|
154
|
+
res = (operand0, operand1)
|
|
155
|
+
return res
|
|
156
|
+
|
|
157
|
+
@_("NULL")
|
|
158
|
+
def operand(self, p: YaccProduction) -> None:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
@_("INT") # noqa: 821
|
|
162
|
+
def operand(self, p: YaccProduction) -> int: # noqa: 811
|
|
163
|
+
return int(p.INT)
|
|
164
|
+
|
|
165
|
+
@_("BOOL")
|
|
166
|
+
def operand(self, p: YaccProduction) -> bool:
|
|
167
|
+
val = p.BOOL.lower()
|
|
168
|
+
return val == "true"
|
|
169
|
+
|
|
170
|
+
@_("array")
|
|
171
|
+
def operand(self, p: YaccProduction) -> List[Any]:
|
|
172
|
+
return _ArrayType(p.array)
|
|
173
|
+
|
|
174
|
+
@_('"{" operand "}"')
|
|
175
|
+
def array(self, p: YaccProduction) -> List[Any]:
|
|
176
|
+
if isinstance(p.operand, Sequence):
|
|
177
|
+
return _ArrayType(p.operand)
|
|
178
|
+
|
|
179
|
+
return _ArrayType([p.operand])
|
|
180
|
+
|
|
181
|
+
@_('array "," operand')
|
|
182
|
+
def operand(self, p: YaccProduction) -> List[Any]:
|
|
183
|
+
# if isinstance(p.operand, tuple):
|
|
184
|
+
|
|
185
|
+
if isinstance(p.operand, tuple):
|
|
186
|
+
return (p.array,) + p.operand
|
|
187
|
+
|
|
188
|
+
return (p.array, p.operand)
|
|
189
|
+
|
|
190
|
+
@_("STRING") # noqa: 821
|
|
191
|
+
def operand(self, p: YaccProduction) -> str: # noqa: 811
|
|
192
|
+
val = p.STRING
|
|
193
|
+
if val.startswith('"'):
|
|
194
|
+
val = val[1:-1]
|
|
195
|
+
|
|
196
|
+
return val
|
|
197
|
+
|
|
198
|
+
@_("path") # noqa: 821
|
|
199
|
+
def operand(self, p: YaccProduction) -> Any: # noqa: 811
|
|
200
|
+
return p.path
|
|
201
|
+
|
|
202
|
+
@_("USER")
|
|
203
|
+
def path(self, _) -> Dict[str, Any]:
|
|
204
|
+
return self.__user_profile
|
|
205
|
+
|
|
206
|
+
@_("NAME") # noqa: 821
|
|
207
|
+
def path(self, p: YaccProduction) -> Any: # noqa: 811
|
|
208
|
+
NAME = p.NAME
|
|
209
|
+
|
|
210
|
+
if not hasattr(p, "path"):
|
|
211
|
+
path = {}
|
|
212
|
+
|
|
213
|
+
return path.get(NAME) if path is not None else None
|
|
214
|
+
|
|
215
|
+
@_('path "." NAME') # noqa: 821
|
|
216
|
+
def operand(self, p: YaccProduction) -> Any: # noqa: 811
|
|
217
|
+
path = p.path
|
|
218
|
+
NAME = p.NAME
|
|
219
|
+
|
|
220
|
+
if isinstance(path, dict):
|
|
221
|
+
res = path.get(NAME)
|
|
222
|
+
else:
|
|
223
|
+
res = path
|
|
224
|
+
|
|
225
|
+
if isinstance(res, str):
|
|
226
|
+
res = res.replace('"', "")
|
|
227
|
+
|
|
228
|
+
return res
|
|
229
|
+
|
|
230
|
+
@_("cls")
|
|
231
|
+
def operand(self, p: YaccProduction) -> Any:
|
|
232
|
+
"""
|
|
233
|
+
Returns a class from `self._expression_classes`
|
|
234
|
+
"""
|
|
235
|
+
return p.cls
|
|
236
|
+
|
|
237
|
+
@_("CLASS")
|
|
238
|
+
def cls(self, p: YaccProduction) -> type:
|
|
239
|
+
"""Returns a class from `self._expression_classes`"""
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
return getattr(self._expression_classes, p.CLASS)
|
|
243
|
+
except AttributeError:
|
|
244
|
+
raise NotImplemented(
|
|
245
|
+
f"Class {p.CLASS} has not been implemented in the parser"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
@_('cls "." NAME "(" operand ")"')
|
|
249
|
+
@_('cls "." NAME "(" path ")"')
|
|
250
|
+
def operand(self, p: YaccProduction) -> Any:
|
|
251
|
+
"""Calls a method from `cls`, which is a class from `self._expression_classes`"""
|
|
252
|
+
method = getattr(p.cls, p.NAME)
|
|
253
|
+
|
|
254
|
+
if hasattr(p, "operand"):
|
|
255
|
+
val = p.operand
|
|
256
|
+
else:
|
|
257
|
+
val = p.path
|
|
258
|
+
|
|
259
|
+
if isinstance(val, tuple):
|
|
260
|
+
res = method(*val)
|
|
261
|
+
|
|
262
|
+
else:
|
|
263
|
+
res = method(val)
|
|
264
|
+
|
|
265
|
+
return res
|
|
266
|
+
|
|
267
|
+
@_('cls "." NAME "(" operand ")"')
|
|
268
|
+
@_('cls "." NAME "(" path ")"')
|
|
269
|
+
def condition(self, p: YaccProduction) -> bool:
|
|
270
|
+
"""Calls a method from `cls`, which is a class from `self._expression_classes`"""
|
|
271
|
+
method = getattr(p.cls, p.NAME)
|
|
272
|
+
|
|
273
|
+
if hasattr(p, "operand"):
|
|
274
|
+
val = p.operand
|
|
275
|
+
else:
|
|
276
|
+
val = p.path
|
|
277
|
+
|
|
278
|
+
if isinstance(val, tuple):
|
|
279
|
+
res = method(*val)
|
|
280
|
+
|
|
281
|
+
else:
|
|
282
|
+
res = method(val)
|
|
283
|
+
|
|
284
|
+
return res
|
|
285
|
+
|
|
286
|
+
@_('MEMBEROFANY "(" operand ")"')
|
|
287
|
+
def condition(self, p: YaccProduction):
|
|
288
|
+
"""
|
|
289
|
+
Tests if a user is a member of one or many groups
|
|
290
|
+
"""
|
|
291
|
+
if not self.__group_ids:
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
val = p.operand
|
|
295
|
+
if not isinstance(val, list):
|
|
296
|
+
val = [val]
|
|
297
|
+
|
|
298
|
+
res = list(set(self.__group_ids) & set(val))
|
|
299
|
+
|
|
300
|
+
return bool(res)
|
|
301
|
+
|
|
302
|
+
@_('MEMBEROF "(" operand ")"')
|
|
303
|
+
def condition(self, p: YaccProduction):
|
|
304
|
+
"""Tests if a user is a member of a specific group"""
|
|
305
|
+
|
|
306
|
+
if not self.__group_ids:
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
# return p.operand.lower() in self.__group_ids
|
|
310
|
+
return p.operand in self.__group_ids
|
|
311
|
+
|
|
312
|
+
@_("condition error") # noqa: 821
|
|
313
|
+
def operand(self, x): # noqa: 811
|
|
314
|
+
raise SyntaxError(
|
|
315
|
+
f"Unexpected token '{x.error.value}' on line {x.error.lineno}"
|
|
316
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "okta-expression-parser"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Returns a boolean by passing Okta profile data to an Okta Expression using Okta Expression Language"
|
|
5
|
+
authors = ["Mathew Moon <me@mathewmoon.net>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "okta_expression_parser", from = "."}]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "^3.11"
|
|
11
|
+
sly = "^0.5"
|
|
12
|
+
|
|
13
|
+
[tool.poetry.group.dev.dependencies]
|
|
14
|
+
pytest = "7.4.3"
|
|
15
|
+
|
|
16
|
+
[tool.pytest.ini_options]
|
|
17
|
+
xfail_strict = true
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["poetry-core>=1.7.0"]
|
|
21
|
+
build-backend = "poetry.core.masonry.api"
|