PyPANRestV2 2.1.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.
- pypanrestv2-2.1.0/PKG-INFO +209 -0
- pypanrestv2-2.1.0/README.md +186 -0
- pypanrestv2-2.1.0/pypanrestv2/ApplicationHelper.py +220 -0
- pypanrestv2-2.1.0/pypanrestv2/Base.py +1772 -0
- pypanrestv2-2.1.0/pypanrestv2/Device.py +21 -0
- pypanrestv2-2.1.0/pypanrestv2/Exceptions.py +11 -0
- pypanrestv2-2.1.0/pypanrestv2/Network.py +1722 -0
- pypanrestv2-2.1.0/pypanrestv2/Objects.py +1428 -0
- pypanrestv2-2.1.0/pypanrestv2/Panorama.py +426 -0
- pypanrestv2-2.1.0/pypanrestv2/Policies.py +755 -0
- pypanrestv2-2.1.0/pypanrestv2/XDR.py +299 -0
- pypanrestv2-2.1.0/pypanrestv2/__init__.py +4 -0
- pypanrestv2-2.1.0/pyproject.toml +23 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: PyPANRestV2
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Python tools for interacting with Palo Alto Networks REST API.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Mark Rzepa
|
|
7
|
+
Author-email: mark@rzepa.com
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Dist: dnspython (>=2.6.1)
|
|
14
|
+
Requires-Dist: icecream (>=2.1.3)
|
|
15
|
+
Requires-Dist: pycountry (>=23.12.11)
|
|
16
|
+
Requires-Dist: python-dotenv (>=1.0.1)
|
|
17
|
+
Requires-Dist: requests (>=2.31.0)
|
|
18
|
+
Requires-Dist: tqdm (>=4.66.2)
|
|
19
|
+
Requires-Dist: validators (>=0.22.0)
|
|
20
|
+
Requires-Dist: xmltodict (>=0.13.0)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# PyPanRestV2
|
|
24
|
+
|
|
25
|
+
**PyPanRestV2** is a Python library designed to simplify interactions with Palo Alto Networks firewalls and Panorama via their REST API. It provides a higher level of abstraction, allowing users to manage firewalls and Panorama without needing to construct REST requests manually or work with XML for areas of the firewall configuration that still require it.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **High-Level Abstraction**: Simplifies interaction with the Palo Alto Networks API.
|
|
32
|
+
- **Support for Firewalls and Panorama**: Manage both individual firewalls and Panorama devices.
|
|
33
|
+
- **REST API Integration**: Allows seamless communication with devices.
|
|
34
|
+
- **Convenient Pythonic Objects**: Intuitive Python objects for interacting with specific sections of Palo Alto firewall configurations.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
To install `PyPanRestV2`, clone the repository and install it as a package:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Clone the repository
|
|
44
|
+
git clone https://github.com/mrzepa/pypanrestv2.git
|
|
45
|
+
|
|
46
|
+
# Navigate to the project directory
|
|
47
|
+
cd pypanrestv2
|
|
48
|
+
|
|
49
|
+
# Install the package in development mode
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This will install the package and all required dependencies automatically. The `-e` flag installs the package in "editable" mode, which is useful if you plan to modify the code or contribute to the project.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Basic Usage
|
|
58
|
+
|
|
59
|
+
### Import the Required Classes
|
|
60
|
+
Start by importing the necessary classes from the library:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from pypantrestv2 import Firewall, Panorama
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Connect to a Firewall or Panorama Device
|
|
67
|
+
Create a `Firewall` or `Panorama` object by providing the required connection details:
|
|
68
|
+
|
|
69
|
+
For a **Firewall**:
|
|
70
|
+
```python
|
|
71
|
+
firewall = Firewall(base_url="192.168.1.1", api_key="12345")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For **Panorama**:
|
|
75
|
+
```python
|
|
76
|
+
panorama = Panorama(base_url="192.168.2.1", username="admin", password="my_password")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Common Use Cases
|
|
80
|
+
|
|
81
|
+
#### 1. Managing Security Rules
|
|
82
|
+
```python
|
|
83
|
+
from pypanrestv2.Policies import SecurityRules
|
|
84
|
+
|
|
85
|
+
# Create a new security rule
|
|
86
|
+
security_rule = SecurityRules(firewall, name='allow_web')
|
|
87
|
+
security_rule.source_zone = ['trust']
|
|
88
|
+
security_rule.destination_zone = ['untrust']
|
|
89
|
+
security_rule.source = ['any']
|
|
90
|
+
security_rule.destination = ['any']
|
|
91
|
+
security_rule.application = ['web-browsing']
|
|
92
|
+
security_rule.service = ['application-default']
|
|
93
|
+
security_rule.action = 'allow'
|
|
94
|
+
security_rule.create()
|
|
95
|
+
|
|
96
|
+
# Modify an existing rule
|
|
97
|
+
existing_rule = SecurityRules(firewall, name='existing_rule')
|
|
98
|
+
existing_rule.refresh() # Load current configuration
|
|
99
|
+
existing_rule.action = 'deny'
|
|
100
|
+
existing_rule.update()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 2. Managing Address Objects
|
|
104
|
+
```python
|
|
105
|
+
from pypanrestv2.Objects import Addresses
|
|
106
|
+
|
|
107
|
+
# Create a new address object
|
|
108
|
+
address = Addresses(firewall, name='web_server')
|
|
109
|
+
address.value = '192.168.1.100'
|
|
110
|
+
address.type = 'ip-netmask'
|
|
111
|
+
address.create()
|
|
112
|
+
|
|
113
|
+
# Get all address objects
|
|
114
|
+
all_addresses = Addresses.get_all(firewall)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### 3. Working with Panorama Device Groups
|
|
118
|
+
```python
|
|
119
|
+
from pypanrestv2 import Panorama
|
|
120
|
+
|
|
121
|
+
# Initialize Panorama connection
|
|
122
|
+
panorama = Panorama(base_url='panorama.example.com', api_key='YOUR_API_KEY')
|
|
123
|
+
|
|
124
|
+
# Add a device to a device group
|
|
125
|
+
device_group = panorama.device_groups.get('Branch_Offices')
|
|
126
|
+
device_group.add_device('serial123')
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Repository
|
|
132
|
+
|
|
133
|
+
Visit the project's GitHub repository for source code, documentation, enhancements, and contributions:
|
|
134
|
+
|
|
135
|
+
[PyPanRestV2 Repository on GitHub](https://github.com/wellhealthtechnologies/PyPanRestV2.git)
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Requirements
|
|
140
|
+
|
|
141
|
+
- **Python 3.11+** (or higher)
|
|
142
|
+
- **Palo Alto Devices** or Panorama
|
|
143
|
+
- Python modules listed in requirements.txt
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## API Documentation
|
|
148
|
+
|
|
149
|
+
The SDK provides access to the following main components:
|
|
150
|
+
|
|
151
|
+
### Core Modules
|
|
152
|
+
- `Firewall/Panorama`: Base connection and authentication
|
|
153
|
+
- `Policies`: Security rules, NAT rules, and policy management
|
|
154
|
+
- `Objects`: Address objects, service objects, and security profiles
|
|
155
|
+
- `Network`: Interfaces, zones, and routing configuration
|
|
156
|
+
- `Device`: System settings and device management
|
|
157
|
+
|
|
158
|
+
### Error Handling
|
|
159
|
+
|
|
160
|
+
The SDK uses custom exceptions for better error handling:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from pypanrestv2.Exceptions import PANConnectionError, PANConfigError
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
firewall = Firewall(base_url='192.168.1.1', api_key='invalid_key')
|
|
167
|
+
firewall.test_connection()
|
|
168
|
+
except PANConnectionError as e:
|
|
169
|
+
print(f'Connection failed: {e}')
|
|
170
|
+
except PANConfigError as e:
|
|
171
|
+
print(f'Configuration error: {e}')
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Common errors and solutions:
|
|
175
|
+
- `PANConnectionError`: Check network connectivity and API credentials
|
|
176
|
+
- `PANConfigError`: Verify object names and configuration values
|
|
177
|
+
- `PANNotFoundError`: Ensure referenced objects exist
|
|
178
|
+
|
|
179
|
+
## Status and Updates
|
|
180
|
+
|
|
181
|
+
This SDK is actively maintained and regularly updated to support new PAN-OS versions. While not all API endpoints are implemented, core functionality is stable and production-ready. Check the GitHub repository for the latest updates and supported features.
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
Contributions are welcome! If you want to report issues, request features, or contribute to the library:
|
|
186
|
+
|
|
187
|
+
1. Fork the repository.
|
|
188
|
+
2. Create a feature branch: `git checkout -b my-feature`.
|
|
189
|
+
3. Commit your changes: `git commit -m "Add detailed description of changes"`.
|
|
190
|
+
4. Push to the branch: `git push origin my-feature`.
|
|
191
|
+
5. Submit a pull request.
|
|
192
|
+
|
|
193
|
+
Be sure to check the documentation, if provided, before starting contributions.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
This project is licensed under the MIT. See the [LICENSE](./https://opensource.org/license/mit) file for details.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Author
|
|
204
|
+
|
|
205
|
+
Mark Rzepa
|
|
206
|
+
mark@rzepa.com
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# PyPanRestV2
|
|
2
|
+
|
|
3
|
+
**PyPanRestV2** is a Python library designed to simplify interactions with Palo Alto Networks firewalls and Panorama via their REST API. It provides a higher level of abstraction, allowing users to manage firewalls and Panorama without needing to construct REST requests manually or work with XML for areas of the firewall configuration that still require it.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **High-Level Abstraction**: Simplifies interaction with the Palo Alto Networks API.
|
|
10
|
+
- **Support for Firewalls and Panorama**: Manage both individual firewalls and Panorama devices.
|
|
11
|
+
- **REST API Integration**: Allows seamless communication with devices.
|
|
12
|
+
- **Convenient Pythonic Objects**: Intuitive Python objects for interacting with specific sections of Palo Alto firewall configurations.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
To install `PyPanRestV2`, clone the repository and install it as a package:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Clone the repository
|
|
22
|
+
git clone https://github.com/mrzepa/pypanrestv2.git
|
|
23
|
+
|
|
24
|
+
# Navigate to the project directory
|
|
25
|
+
cd pypanrestv2
|
|
26
|
+
|
|
27
|
+
# Install the package in development mode
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This will install the package and all required dependencies automatically. The `-e` flag installs the package in "editable" mode, which is useful if you plan to modify the code or contribute to the project.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Basic Usage
|
|
36
|
+
|
|
37
|
+
### Import the Required Classes
|
|
38
|
+
Start by importing the necessary classes from the library:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from pypantrestv2 import Firewall, Panorama
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Connect to a Firewall or Panorama Device
|
|
45
|
+
Create a `Firewall` or `Panorama` object by providing the required connection details:
|
|
46
|
+
|
|
47
|
+
For a **Firewall**:
|
|
48
|
+
```python
|
|
49
|
+
firewall = Firewall(base_url="192.168.1.1", api_key="12345")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For **Panorama**:
|
|
53
|
+
```python
|
|
54
|
+
panorama = Panorama(base_url="192.168.2.1", username="admin", password="my_password")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Common Use Cases
|
|
58
|
+
|
|
59
|
+
#### 1. Managing Security Rules
|
|
60
|
+
```python
|
|
61
|
+
from pypanrestv2.Policies import SecurityRules
|
|
62
|
+
|
|
63
|
+
# Create a new security rule
|
|
64
|
+
security_rule = SecurityRules(firewall, name='allow_web')
|
|
65
|
+
security_rule.source_zone = ['trust']
|
|
66
|
+
security_rule.destination_zone = ['untrust']
|
|
67
|
+
security_rule.source = ['any']
|
|
68
|
+
security_rule.destination = ['any']
|
|
69
|
+
security_rule.application = ['web-browsing']
|
|
70
|
+
security_rule.service = ['application-default']
|
|
71
|
+
security_rule.action = 'allow'
|
|
72
|
+
security_rule.create()
|
|
73
|
+
|
|
74
|
+
# Modify an existing rule
|
|
75
|
+
existing_rule = SecurityRules(firewall, name='existing_rule')
|
|
76
|
+
existing_rule.refresh() # Load current configuration
|
|
77
|
+
existing_rule.action = 'deny'
|
|
78
|
+
existing_rule.update()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### 2. Managing Address Objects
|
|
82
|
+
```python
|
|
83
|
+
from pypanrestv2.Objects import Addresses
|
|
84
|
+
|
|
85
|
+
# Create a new address object
|
|
86
|
+
address = Addresses(firewall, name='web_server')
|
|
87
|
+
address.value = '192.168.1.100'
|
|
88
|
+
address.type = 'ip-netmask'
|
|
89
|
+
address.create()
|
|
90
|
+
|
|
91
|
+
# Get all address objects
|
|
92
|
+
all_addresses = Addresses.get_all(firewall)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### 3. Working with Panorama Device Groups
|
|
96
|
+
```python
|
|
97
|
+
from pypanrestv2 import Panorama
|
|
98
|
+
|
|
99
|
+
# Initialize Panorama connection
|
|
100
|
+
panorama = Panorama(base_url='panorama.example.com', api_key='YOUR_API_KEY')
|
|
101
|
+
|
|
102
|
+
# Add a device to a device group
|
|
103
|
+
device_group = panorama.device_groups.get('Branch_Offices')
|
|
104
|
+
device_group.add_device('serial123')
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Repository
|
|
110
|
+
|
|
111
|
+
Visit the project's GitHub repository for source code, documentation, enhancements, and contributions:
|
|
112
|
+
|
|
113
|
+
[PyPanRestV2 Repository on GitHub](https://github.com/wellhealthtechnologies/PyPanRestV2.git)
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Requirements
|
|
118
|
+
|
|
119
|
+
- **Python 3.11+** (or higher)
|
|
120
|
+
- **Palo Alto Devices** or Panorama
|
|
121
|
+
- Python modules listed in requirements.txt
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## API Documentation
|
|
126
|
+
|
|
127
|
+
The SDK provides access to the following main components:
|
|
128
|
+
|
|
129
|
+
### Core Modules
|
|
130
|
+
- `Firewall/Panorama`: Base connection and authentication
|
|
131
|
+
- `Policies`: Security rules, NAT rules, and policy management
|
|
132
|
+
- `Objects`: Address objects, service objects, and security profiles
|
|
133
|
+
- `Network`: Interfaces, zones, and routing configuration
|
|
134
|
+
- `Device`: System settings and device management
|
|
135
|
+
|
|
136
|
+
### Error Handling
|
|
137
|
+
|
|
138
|
+
The SDK uses custom exceptions for better error handling:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from pypanrestv2.Exceptions import PANConnectionError, PANConfigError
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
firewall = Firewall(base_url='192.168.1.1', api_key='invalid_key')
|
|
145
|
+
firewall.test_connection()
|
|
146
|
+
except PANConnectionError as e:
|
|
147
|
+
print(f'Connection failed: {e}')
|
|
148
|
+
except PANConfigError as e:
|
|
149
|
+
print(f'Configuration error: {e}')
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Common errors and solutions:
|
|
153
|
+
- `PANConnectionError`: Check network connectivity and API credentials
|
|
154
|
+
- `PANConfigError`: Verify object names and configuration values
|
|
155
|
+
- `PANNotFoundError`: Ensure referenced objects exist
|
|
156
|
+
|
|
157
|
+
## Status and Updates
|
|
158
|
+
|
|
159
|
+
This SDK is actively maintained and regularly updated to support new PAN-OS versions. While not all API endpoints are implemented, core functionality is stable and production-ready. Check the GitHub repository for the latest updates and supported features.
|
|
160
|
+
|
|
161
|
+
## Contributing
|
|
162
|
+
|
|
163
|
+
Contributions are welcome! If you want to report issues, request features, or contribute to the library:
|
|
164
|
+
|
|
165
|
+
1. Fork the repository.
|
|
166
|
+
2. Create a feature branch: `git checkout -b my-feature`.
|
|
167
|
+
3. Commit your changes: `git commit -m "Add detailed description of changes"`.
|
|
168
|
+
4. Push to the branch: `git push origin my-feature`.
|
|
169
|
+
5. Submit a pull request.
|
|
170
|
+
|
|
171
|
+
Be sure to check the documentation, if provided, before starting contributions.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
This project is licensed under the MIT. See the [LICENSE](./https://opensource.org/license/mit) file for details.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Author
|
|
182
|
+
|
|
183
|
+
Mark Rzepa
|
|
184
|
+
mark@rzepa.com
|
|
185
|
+
|
|
186
|
+
---
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
class Operator:
|
|
2
|
+
def __init__(self, context):
|
|
3
|
+
self.context = context
|
|
4
|
+
|
|
5
|
+
@property
|
|
6
|
+
def context(self) -> str:
|
|
7
|
+
return self._context
|
|
8
|
+
|
|
9
|
+
@context.setter
|
|
10
|
+
def context(self, value):
|
|
11
|
+
if not isinstance(value, str):
|
|
12
|
+
raise ValueError("Context must be a string.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PatternMatchOperator(Operator):
|
|
16
|
+
def __init__(self, context, pattern, qualifier):
|
|
17
|
+
super().__init__(context)
|
|
18
|
+
self.pattern = pattern
|
|
19
|
+
self.qualifier = self.initialize_qualifier(qualifier)
|
|
20
|
+
|
|
21
|
+
def initialize_qualifier(self, qualifier):
|
|
22
|
+
if 'entry' in qualifier:
|
|
23
|
+
return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
|
|
24
|
+
return []
|
|
25
|
+
|
|
26
|
+
def to_dict(self):
|
|
27
|
+
return {
|
|
28
|
+
'type': 'pattern-match',
|
|
29
|
+
'context': self.context,
|
|
30
|
+
'pattern': self.pattern,
|
|
31
|
+
'qualifier': {'entry': self.qualifier},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GreaterThanOperator(Operator):
|
|
36
|
+
def __init__(self, context, value, qualifier):
|
|
37
|
+
super().__init__(context)
|
|
38
|
+
self.value = value
|
|
39
|
+
self.validate_value(value)
|
|
40
|
+
self.qualifier = self.initialize_qualifier(qualifier)
|
|
41
|
+
|
|
42
|
+
def validate_value(self, value):
|
|
43
|
+
if not isinstance(value, int) or not (0 <= value <= 4294967295):
|
|
44
|
+
raise ValueError("Value must be an integer between 0 and 4294967295.")
|
|
45
|
+
|
|
46
|
+
def initialize_qualifier(self, qualifier):
|
|
47
|
+
if 'entry' in qualifier:
|
|
48
|
+
return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
def to_dict(self):
|
|
52
|
+
return {
|
|
53
|
+
'type': 'greater-than',
|
|
54
|
+
'context': self.context,
|
|
55
|
+
'value': self.value,
|
|
56
|
+
'qualifier': {'entry': self.qualifier},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LessThanOperator(Operator):
|
|
61
|
+
def __init__(self, context, value, qualifier):
|
|
62
|
+
super().__init__(context)
|
|
63
|
+
self.value = value
|
|
64
|
+
self.validate_value(value)
|
|
65
|
+
self.qualifier = self.initialize_qualifier(qualifier)
|
|
66
|
+
|
|
67
|
+
def validate_value(self, value):
|
|
68
|
+
if not isinstance(value, int) or not (0 <= value <= 4294967295):
|
|
69
|
+
raise ValueError("Value must be an integer between 0 and 4294967295.")
|
|
70
|
+
|
|
71
|
+
def initialize_qualifier(self, qualifier):
|
|
72
|
+
if 'entry' in qualifier:
|
|
73
|
+
return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
def to_dict(self):
|
|
77
|
+
return {
|
|
78
|
+
'type': 'less-than',
|
|
79
|
+
'context': self.context,
|
|
80
|
+
'value': self.value,
|
|
81
|
+
'qualifier': {'entry': self.qualifier},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class EqualToOperator(Operator):
|
|
86
|
+
def __init__(self, context, position, mask, value):
|
|
87
|
+
super().__init__(context)
|
|
88
|
+
self.position = position
|
|
89
|
+
self.mask = mask
|
|
90
|
+
self.value = value
|
|
91
|
+
self.validate_position(position)
|
|
92
|
+
self.validate_mask(mask)
|
|
93
|
+
self.validate_value(value)
|
|
94
|
+
|
|
95
|
+
def validate_position(self, position):
|
|
96
|
+
if not isinstance(position, str) or len(position) > 127:
|
|
97
|
+
raise ValueError("Position must be a string up to 127 characters long.")
|
|
98
|
+
|
|
99
|
+
def validate_mask(self, mask):
|
|
100
|
+
if not isinstance(mask, str) or not re.match(r'^0[xX][0-9A-Fa-f]{8}$', mask):
|
|
101
|
+
raise ValueError("Mask must be a 4-byte hex value.")
|
|
102
|
+
|
|
103
|
+
def validate_value(self, value):
|
|
104
|
+
if not isinstance(value, str) or len(value) > 10:
|
|
105
|
+
raise ValueError("Value must be a string up to 10 characters long.")
|
|
106
|
+
|
|
107
|
+
def to_dict(self):
|
|
108
|
+
return {
|
|
109
|
+
'type': 'equal-to',
|
|
110
|
+
'context': self.context,
|
|
111
|
+
'position': self.position,
|
|
112
|
+
'mask': self.mask,
|
|
113
|
+
'value': self.value,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class OrConditionEntry:
|
|
117
|
+
def __init__(self, name, operator_data):
|
|
118
|
+
self.validate_name(name)
|
|
119
|
+
self.name = name
|
|
120
|
+
self.operator = self.initialize_operator(operator_data)
|
|
121
|
+
|
|
122
|
+
def validate_name(self, name):
|
|
123
|
+
if not isinstance(name, str) or len(name) > 31:
|
|
124
|
+
raise ValueError("Invalid name in or-condition entry.")
|
|
125
|
+
|
|
126
|
+
def initialize_operator(self, operator_data):
|
|
127
|
+
if not isinstance(operator_data, dict):
|
|
128
|
+
raise ValueError("Operator data must be a dictionary.")
|
|
129
|
+
|
|
130
|
+
operator_type = operator_data.get('type')
|
|
131
|
+
if operator_type == 'pattern-match':
|
|
132
|
+
return PatternMatchOperator(context=operator_data.get('context'),
|
|
133
|
+
pattern=operator_data.get('pattern'),
|
|
134
|
+
qualifier=operator_data.get('qualifier'))
|
|
135
|
+
elif operator_type == 'greater-than':
|
|
136
|
+
return GreaterThanOperator(context=operator_data.get('context'),
|
|
137
|
+
value=operator_data.get('value'),
|
|
138
|
+
qualifier=operator_data.get('qualifier'))
|
|
139
|
+
elif operator_type == 'less-than':
|
|
140
|
+
return GreaterThanOperator(context=operator_data.get('context'),
|
|
141
|
+
value=operator_data.get('value'),
|
|
142
|
+
qualifier=operator_data.get('qualifier'))
|
|
143
|
+
elif operator_type == 'equal-to':
|
|
144
|
+
return EqualToOperator(context=operator_data.get('context'),
|
|
145
|
+
position=operator_data.get('position'),
|
|
146
|
+
mask=operator_data.get('mask'),
|
|
147
|
+
value=operator_data.get('value'))
|
|
148
|
+
else:
|
|
149
|
+
raise ValueError(f"Unsupported operator type: {operator_type}")
|
|
150
|
+
|
|
151
|
+
def to_dict(self):
|
|
152
|
+
# Convert the OrConditionEntry instance to a dictionary format
|
|
153
|
+
or_condition_dict = {
|
|
154
|
+
'name': self.name,
|
|
155
|
+
# Include operator serialization here once operator.to_dict() is defined
|
|
156
|
+
'operator': self.operator.to_dict() if self.operator else None,
|
|
157
|
+
}
|
|
158
|
+
return or_condition_dict
|
|
159
|
+
|
|
160
|
+
class AndConditionEntry:
|
|
161
|
+
def __init__(self, name, or_condition=None):
|
|
162
|
+
self.validate_name(name)
|
|
163
|
+
self.name = name
|
|
164
|
+
self.or_condition = self.initialize_or_condition(or_condition)
|
|
165
|
+
|
|
166
|
+
def validate_name(self, name):
|
|
167
|
+
if not isinstance(name, str) or len(name) > 31:
|
|
168
|
+
raise ValueError("Invalid name in and-condition entry.")
|
|
169
|
+
# Ensures the name consists only of allowed characters
|
|
170
|
+
if not all(char.isalnum() or char in "._-" for char in name):
|
|
171
|
+
raise ValueError("Name in and-condition entry contains invalid characters.")
|
|
172
|
+
|
|
173
|
+
def initialize_or_condition(self, or_condition):
|
|
174
|
+
if or_condition and 'entry' in or_condition:
|
|
175
|
+
return [OrConditionEntry(**entry) for entry in or_condition['entry']]
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
def to_dict(self):
|
|
179
|
+
# Convert the AndConditionEntry instance to a dictionary format
|
|
180
|
+
and_condition_dict = {
|
|
181
|
+
'name': self.name,
|
|
182
|
+
# Serialize or-condition if it's present
|
|
183
|
+
'or-condition': {'entry': [condition.to_dict() for condition in self.or_condition] if self.or_condition else None},
|
|
184
|
+
}
|
|
185
|
+
return and_condition_dict
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class SignatureEntry:
|
|
189
|
+
def __init__(self, name, comment="", scope="protocol-data-unit", order_free="no", and_condition=None):
|
|
190
|
+
self.validate_name(name)
|
|
191
|
+
self.name = name
|
|
192
|
+
self.comment = comment
|
|
193
|
+
self.scope = self.validate_scope(scope)
|
|
194
|
+
self.order_free = self.validate_order_free(order_free)
|
|
195
|
+
self.and_condition = self.initialize_and_condition(and_condition)
|
|
196
|
+
|
|
197
|
+
def to_dict(self):
|
|
198
|
+
# Convert the SignatureEntry instance to a dictionary format suitable for inclusion in self.entry
|
|
199
|
+
signature_dict = {
|
|
200
|
+
'name': self.name,
|
|
201
|
+
'comment': self.comment,
|
|
202
|
+
'scope': self.scope,
|
|
203
|
+
'order-free': self.order_free,
|
|
204
|
+
'and-condition': {'entry': [condition.to_dict() for condition in self.and_condition] if self.and_condition else None},
|
|
205
|
+
}
|
|
206
|
+
return signature_dict
|
|
207
|
+
|
|
208
|
+
def validate_name(self, name):
|
|
209
|
+
if not isinstance(name, str) or len(name) > 31 or not all(char.isalnum() or char in "._-" for char in name):
|
|
210
|
+
raise ValueError("Invalid name for signature entry.")
|
|
211
|
+
|
|
212
|
+
def validate_scope(self, scope):
|
|
213
|
+
if scope not in ["protocol-data-unit", "session"]:
|
|
214
|
+
raise ValueError("Invalid scope value.")
|
|
215
|
+
return scope
|
|
216
|
+
|
|
217
|
+
def validate_order_free(self, order_free):
|
|
218
|
+
if order_free not in ["yes", "no"]:
|
|
219
|
+
raise ValueError("Invalid order-free value.")
|
|
220
|
+
return order_free
|