graylog-api-client 0.1.4__tar.gz → 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.
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/PKG-INFO +3 -2
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/README.md +1 -1
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/pyproject.toml +1 -1
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/data_structures.py +1 -1
- graylog_api_client-0.2.0/src/graylog_api_client/endpoints/authz.py +27 -0
- graylog_api_client-0.2.0/src/graylog_api_client/endpoints/plugins/SecurityApp.py +101 -0
- graylog_api_client-0.2.0/src/graylog_api_client/endpoints/plugins/__init__.py +7 -0
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/graylog_api_client.py +10 -5
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/rest_adapter.py +3 -3
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/LICENSE +0 -0
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/__init__.py +0 -0
- {graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graylog-api-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Python wrapper for the Graylog REST API
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -11,6 +11,7 @@ Requires-Python: >=3.13
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
14
15
|
Requires-Dist: requests (>=2.32.5,<3.0.0)
|
|
15
16
|
Project-URL: Repository, https://github.com/Chrxxxxs/graylog_api_client
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
@@ -19,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
19
20
|
As there are a lot of endpoints not all of them are implemented by now. I started by implementing the GET requests first.
|
|
20
21
|
Also the API responses all follow very different schemas and an implementation of that into Python is probably not worth the effort which is why the responses from this API are so low-level.
|
|
21
22
|
Feel free to add endpoints or schemas in PRs if you need them :)
|
|
22
|
-
#### Last tested Graylog Version is:
|
|
23
|
+
#### Last tested Graylog Major Version is: 7
|
|
23
24
|
|
|
24
25
|
### Example Usage
|
|
25
26
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
As there are a lot of endpoints not all of them are implemented by now. I started by implementing the GET requests first.
|
|
3
3
|
Also the API responses all follow very different schemas and an implementation of that into Python is probably not worth the effort which is why the responses from this API are so low-level.
|
|
4
4
|
Feel free to add endpoints or schemas in PRs if you need them :)
|
|
5
|
-
#### Last tested Graylog Version is:
|
|
5
|
+
#### Last tested Graylog Major Version is: 7
|
|
6
6
|
|
|
7
7
|
### Example Usage
|
|
8
8
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ..data_structures import GraylogApiResult
|
|
2
|
+
from ..rest_adapter import RestAdapter
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
class Endpoint_AUTH:
|
|
6
|
+
def __init__(self, rest_adapter: RestAdapter):
|
|
7
|
+
self._rest_adapter = rest_adapter
|
|
8
|
+
|
|
9
|
+
def get_grants_overview(self) -> GraylogApiResult:
|
|
10
|
+
overview = self._rest_adapter.get("authz/grants-overview")
|
|
11
|
+
return overview
|
|
12
|
+
|
|
13
|
+
def get_roles(self, parameters: Dict = None) -> GraylogApiResult:
|
|
14
|
+
roles = self._rest_adapter.get("authz/roles", parameters)
|
|
15
|
+
return roles
|
|
16
|
+
|
|
17
|
+
def get_roles_of_user(self, username: str, parameters: Dict = None) -> GraylogApiResult:
|
|
18
|
+
roles = self._rest_adapter.get(f"authz/roles/user/{username}", parameters)
|
|
19
|
+
return roles
|
|
20
|
+
|
|
21
|
+
def get_role_by_id(self, role_id: str) -> GraylogApiResult:
|
|
22
|
+
roles = self._rest_adapter.get(f"authz/roles/{role_id}")
|
|
23
|
+
return roles
|
|
24
|
+
|
|
25
|
+
def get_assignees_of_role(self, role_id: str, parameters: Dict = None) -> GraylogApiResult:
|
|
26
|
+
assignees = self._rest_adapter.get(f"authz/roles/{role_id}/assignees", parameters)
|
|
27
|
+
return assignees
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from ...data_structures import GraylogApiResult
|
|
2
|
+
from ...rest_adapter import RestAdapter
|
|
3
|
+
from typing import Union, Dict
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
class SecurityApp:
|
|
7
|
+
def __init__(self, rest_adapter: RestAdapter):
|
|
8
|
+
self._rest_adapter = rest_adapter
|
|
9
|
+
self.Assets = Assets(rest_adapter)
|
|
10
|
+
|
|
11
|
+
class Assets:
|
|
12
|
+
def __init__(self, rest_adapter: RestAdapter):
|
|
13
|
+
self._rest_adapter = rest_adapter
|
|
14
|
+
|
|
15
|
+
def get_assets(self, *, sort: str = "name", page: int = 1, per_page: int = 15, query: str = "",\
|
|
16
|
+
filters: str = "", direction: str = "asc") -> GraylogApiResult:
|
|
17
|
+
parameters = {
|
|
18
|
+
"sort": sort,
|
|
19
|
+
"page": page,
|
|
20
|
+
"per_page": per_page,
|
|
21
|
+
"query": query,
|
|
22
|
+
"filters": filters,
|
|
23
|
+
"direction": direction,
|
|
24
|
+
}
|
|
25
|
+
result = self._rest_adapter.get("plugins/org.graylog.plugins.securityapp.asset/assets", parameters)
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
# The API Browser is very confusing/wrong for creating assets
|
|
29
|
+
def create_user_asset(
|
|
30
|
+
self, *, name: str, usernames: list[str], user_ids: list[str] = [], email_addresses: list[str] = [], first_name: str = "", last_name: str = "", category: list[str] = [], priority: int = 1
|
|
31
|
+
) -> GraylogApiResult:
|
|
32
|
+
"""Create a user asset."""
|
|
33
|
+
data = {
|
|
34
|
+
"category": category,
|
|
35
|
+
"details": {
|
|
36
|
+
"type": "user",
|
|
37
|
+
"usernames": usernames,
|
|
38
|
+
"user_ids": user_ids,
|
|
39
|
+
"email_addresses": email_addresses,
|
|
40
|
+
"first_name": first_name,
|
|
41
|
+
"last_name": last_name,
|
|
42
|
+
},
|
|
43
|
+
"name": name,
|
|
44
|
+
"priority": priority,
|
|
45
|
+
}
|
|
46
|
+
result = self._rest_adapter.post("plugins/org.graylog.plugins.securityapp.asset/assets", data=data)
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
def create_machine_asset(
|
|
50
|
+
self, *, name: str, hostnames: list[str], ip_addresses: list[str] = [], mac_addresses: list[str] = [], category: list[str] = [], custom_fields: dict[str, str] = {},
|
|
51
|
+
description: str = "", geo_info: dict[str, int|str] = {}, owner: str = "", priority: int = 1
|
|
52
|
+
) -> GraylogApiResult:
|
|
53
|
+
"""Create a machine asset."""
|
|
54
|
+
data = {
|
|
55
|
+
"category": category,
|
|
56
|
+
"details": {
|
|
57
|
+
"type": "machine",
|
|
58
|
+
"hostnames": hostnames,
|
|
59
|
+
"ip_addresses": ip_addresses,
|
|
60
|
+
"mac_addresses": mac_addresses,
|
|
61
|
+
"custom_fields": custom_fields,
|
|
62
|
+
"description": description,
|
|
63
|
+
"geo_info": geo_info,
|
|
64
|
+
"owner": owner,
|
|
65
|
+
},
|
|
66
|
+
"name": name,
|
|
67
|
+
"priority": priority,
|
|
68
|
+
}
|
|
69
|
+
result = self._rest_adapter.post("plugins/org.graylog.plugins.securityapp.asset/assets", data=data)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
def add_categories_to_multiple_assets(self, *, asset_ids: list[str], categories: list[str]) -> GraylogApiResult:
|
|
73
|
+
# Untested
|
|
74
|
+
data = {
|
|
75
|
+
"asset_ids": asset_ids,
|
|
76
|
+
"categories": categories,
|
|
77
|
+
}
|
|
78
|
+
result = self._rest_adapter.put("plugins/org.graylog.plugins.securityapp.asset/assets/bulk/categories_add", data=data)
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
def get_asset_by_id_detailed(self, asset_id: str) -> GraylogApiResult:
|
|
82
|
+
result = self._rest_adapter.get(
|
|
83
|
+
f"plugins/org.graylog.plugins.securityapp.asset/assets/{asset_id}"
|
|
84
|
+
)
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
def edit_asset_by_id(self, asset_id: str, **updates) -> GraylogApiResult:
|
|
88
|
+
"""Edit the asset with the given ID by passing the updates as keyword arguments."""
|
|
89
|
+
current_state = self.get_asset_by_id_detailed(asset_id)
|
|
90
|
+
payload = current_state.data
|
|
91
|
+
payload.update(updates)
|
|
92
|
+
result = self._rest_adapter.put(
|
|
93
|
+
f"plugins/org.graylog.plugins.securityapp.asset/assets/{asset_id}", data=payload
|
|
94
|
+
)
|
|
95
|
+
return result
|
|
96
|
+
|
|
97
|
+
def delete_asset_by_id(self, asset_id: str) -> GraylogApiResult:
|
|
98
|
+
result = self._rest_adapter.delete(
|
|
99
|
+
f"plugins/org.graylog.plugins.securityapp.asset/assets/{asset_id}"
|
|
100
|
+
)
|
|
101
|
+
return result
|
{graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/graylog_api_client.py
RENAMED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
from typing import List, Literal, Union, Dict
|
|
3
4
|
|
|
4
5
|
from .rest_adapter import RestAdapter
|
|
5
6
|
from .data_structures import GraylogApiResult
|
|
7
|
+
from .endpoints.authz import Endpoint_AUTH
|
|
8
|
+
from .endpoints.plugins import Plugins
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
class GraylogAPI:
|
|
@@ -15,27 +18,29 @@ class GraylogAPI:
|
|
|
15
18
|
:param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
|
|
16
19
|
"""
|
|
17
20
|
self._rest_adapter = RestAdapter(host=host, api_key=api_key, ssl_verify=ssl_verify, logger=logger)
|
|
21
|
+
self.authz = Endpoint_AUTH(rest_adapter=self._rest_adapter)
|
|
22
|
+
self.plugins = Plugins(rest_adapter=self._rest_adapter)
|
|
18
23
|
|
|
19
24
|
# The endpoints grouped like they are in the api-browser
|
|
20
25
|
# /authz Authorization
|
|
21
26
|
def get_auth_grants_overview(self) -> GraylogApiResult:
|
|
22
|
-
overview = self.
|
|
27
|
+
overview = self.authz.get_grants_overview()
|
|
23
28
|
return overview
|
|
24
29
|
|
|
25
30
|
def get_auth_roles(self, parameters: Dict = None) -> GraylogApiResult:
|
|
26
|
-
roles = self.
|
|
31
|
+
roles = self.authz.get_roles(parameters)
|
|
27
32
|
return roles
|
|
28
33
|
|
|
29
34
|
def get_auth_roles_of_user(self, username: str, parameters: Dict = None) -> GraylogApiResult:
|
|
30
|
-
roles = self.
|
|
35
|
+
roles = self.authz.get_roles_of_user(username, parameters)
|
|
31
36
|
return roles
|
|
32
37
|
|
|
33
38
|
def get_auth_role_by_id(self, role_id: str) -> GraylogApiResult:
|
|
34
|
-
roles = self.
|
|
39
|
+
roles = self.authz.get_role_by_id(role_id)
|
|
35
40
|
return roles
|
|
36
41
|
|
|
37
42
|
def get_auth_assignees_of_role(self, role_id: str, parameters: Dict = None) -> GraylogApiResult:
|
|
38
|
-
assignees = self.
|
|
43
|
+
assignees = self.authz.get_assignees_of_role(role_id, parameters)
|
|
39
44
|
return assignees
|
|
40
45
|
|
|
41
46
|
# /ca
|
{graylog_api_client-0.1.4 → graylog_api_client-0.2.0}/src/graylog_api_client/rest_adapter.py
RENAMED
|
@@ -37,7 +37,7 @@ class RestAdapter:
|
|
|
37
37
|
"""
|
|
38
38
|
url = self.host + endpoint
|
|
39
39
|
log_line_pre = f"method={method}, url={url}, parameters={parameters}"
|
|
40
|
-
log_line_post =
|
|
40
|
+
log_line_post = "{}, success={}, status_code={}, message={}"
|
|
41
41
|
try:
|
|
42
42
|
self._logger.debug(msg=log_line_pre)
|
|
43
43
|
response = self._session.request(method=method, url=url, verify=self._ssl_verify, params=parameters, json=data)
|
|
@@ -50,10 +50,10 @@ class RestAdapter:
|
|
|
50
50
|
else:
|
|
51
51
|
data_out = None
|
|
52
52
|
except (ValueError, requests.exceptions.JSONDecodeError) as e:
|
|
53
|
-
self._logger.error(msg=log_line_post.format(False, None, e))
|
|
53
|
+
self._logger.error(msg=log_line_post.format(log_line_pre, False, None, e))
|
|
54
54
|
raise GraylogApiException("Bad JSON in response") from e
|
|
55
55
|
is_success = 299 >= response.status_code >= 200 # OK
|
|
56
|
-
log_line = log_line_post.format(is_success, response.status_code, response.reason)
|
|
56
|
+
log_line = log_line_post.format(log_line_pre, is_success, response.status_code, response.reason)
|
|
57
57
|
if is_success:
|
|
58
58
|
self._logger.debug(msg=log_line)
|
|
59
59
|
return GraylogApiResult(status_code=response.status_code, message=response.reason, data=data_out)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|