graylog-api-client 0.1.3__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.
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Chrxxxxs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chrxxxxs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graylog-api-client
3
- Version: 0.1.3
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: 6.3.4
23
+ #### Last tested Graylog Major Version is: 7
23
24
 
24
25
  ### Example Usage
25
26
 
@@ -1,34 +1,34 @@
1
- ### This is a low level Graylog API Wrapper heavily based on the guide by [pretzel](https://www.pretzellogix.net/2021/12/08/how-to-write-a-python3-sdk-library-module-for-a-json-rest-api/).
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
- 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
- Feel free to add endpoints or schemas in PRs if you need them :)
5
- #### Last tested Graylog Version is: 6.3.4
6
-
7
- ### Example Usage
8
-
9
- ```python
10
- from graylog_api_client import GraylogAPI
11
-
12
- # Initialize the GraylogAPI client
13
- api = GraylogAPI(
14
- host="https://localhost:9000/api", # Replace with your Graylog server URL
15
- api_key="your_api_key_here", # Replace with your API key
16
- ssl_verify=True # /path/to/cert for custom certificate
17
- )
18
-
19
- # Example: Fetch all streams
20
- streams = api.get_streams()
21
-
22
- # Print the result
23
- print("Streams:", streams.data)
24
- ```
25
-
26
- Planned:
27
- 1. Tests that run against a Graylog instance to check if the Wrapper works after Graylog Updates
28
-
29
-
30
- ### Test Environment:
31
- 1. Install docker and docker-compose
32
- 2. Copy the sample docker-compose file by [Graylog](https://github.com/Graylog2/docker-compose) for the open-core (or enterprise if you want to see all API endpoints) and create a .env file with the needed values
33
- 3. docker-compose up
1
+ ### This is a low level Graylog API Wrapper heavily based on the guide by [pretzel](https://www.pretzellogix.net/2021/12/08/how-to-write-a-python3-sdk-library-module-for-a-json-rest-api/).
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
+ 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
+ Feel free to add endpoints or schemas in PRs if you need them :)
5
+ #### Last tested Graylog Major Version is: 7
6
+
7
+ ### Example Usage
8
+
9
+ ```python
10
+ from graylog_api_client import GraylogAPI
11
+
12
+ # Initialize the GraylogAPI client
13
+ api = GraylogAPI(
14
+ host="https://localhost:9000/api", # Replace with your Graylog server URL
15
+ api_key="your_api_key_here", # Replace with your API key
16
+ ssl_verify=True # /path/to/cert for custom certificate
17
+ )
18
+
19
+ # Example: Fetch all streams
20
+ streams = api.get_streams()
21
+
22
+ # Print the result
23
+ print("Streams:", streams.data)
24
+ ```
25
+
26
+ Planned:
27
+ 1. Tests that run against a Graylog instance to check if the Wrapper works after Graylog Updates
28
+
29
+
30
+ ### Test Environment:
31
+ 1. Install docker and docker-compose
32
+ 2. Copy the sample docker-compose file by [Graylog](https://github.com/Graylog2/docker-compose) for the open-core (or enterprise if you want to see all API endpoints) and create a .env file with the needed values
33
+ 3. docker-compose up
34
34
  4. Log in and create an api-key
@@ -1,29 +1,27 @@
1
- [project]
2
- name = "graylog-api-client"
3
- version = "0.1.3"
4
- description = "A Python wrapper for the Graylog REST API"
5
- authors = [
6
- {name = "Chrxxxxs",email = "134285960+Chrxxxxs@users.noreply.github.com"}
7
- ]
8
- keywords = ["graylog", "REST", "API Wrapper"]
9
- license = "MIT"
10
- readme = "README.md"
11
- requires-python = ">=3.13"
12
- dependencies = [
13
- "requests (>=2.32.5,<3.0.0)",
14
- ]
15
-
16
- [project.urls]
17
- Repository = "https://github.com/Chrxxxxs/graylog_api_client"
18
-
19
- [tool.poetry]
20
- packages = [{include = "graylog_api_client", from = "src"}]
21
-
22
- [build-system]
23
- requires = ["poetry-core>=2.0.0,<3.0.0"]
24
- build-backend = "poetry.core.masonry.api"
25
-
26
- [dependency-groups]
27
- dev = [
28
- "pytest>=9.1.1",
29
- ]
1
+ [project]
2
+ name = "graylog-api-client"
3
+ version = "0.2.0"
4
+ description = "A Python wrapper for the Graylog REST API"
5
+ authors = [
6
+ {name = "Chrxxxxs",email = "134285960+Chrxxxxs@users.noreply.github.com"}
7
+ ]
8
+ keywords = ["graylog", "REST", "API Wrapper"]
9
+ license = "MIT"
10
+ readme = "README.md"
11
+ requires-python = ">=3.13"
12
+ dependencies = [
13
+ "requests (>=2.32.5,<3.0.0)",
14
+ ]
15
+
16
+ [project.urls]
17
+ Repository = "https://github.com/Chrxxxxs/graylog_api_client"
18
+
19
+ [tool.poetry]
20
+ packages = [{include = "graylog_api_client", from = "src"}]
21
+
22
+ [build-system]
23
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
24
+ build-backend = "poetry.core.masonry.api"
25
+
26
+ [dependency-groups]
27
+ dev = ["pytest (>=9.1.1,<10.0.0)"]
@@ -1,3 +1,3 @@
1
- from graylog_api_client.graylog_api_client import GraylogAPI
2
-
3
- __all__ = ["GraylogAPI"]
1
+ from graylog_api_client.graylog_api_client import GraylogAPI
2
+
3
+ __all__ = ["GraylogAPI"]
@@ -1,26 +1,26 @@
1
- from typing import List, Dict
2
-
3
-
4
- class GraylogApiResult:
5
- def __init__(self, status_code: int, message: str = "", data: List[Dict] = None):
6
- """Result returned from low-level RestAdapter
7
-
8
- :param status_code: Standard HTTP Status code
9
- :param message: Human readable result
10
- :param data: Python List of Dictionaries (or maybe just a single Dictionary on error)
11
- """
12
- self.status_code = int(status_code)
13
- self.message = str(message)
14
- self.data = data if data else []
15
-
16
-
17
- # TODO make this a proper class
18
- class GraylogTimerange:
19
- # Example relative:
20
- # { "type": "relative", "range": 300 }
21
- # Example absolute:
22
- # { "type": "absolute", "from": "2023-01-01T00:00:00.000Z", "to": "2023-01-02T00:00:00.000Z" }
23
- def relative(range: int):
24
- ...
25
- def absolute(from_time: str, to_time: str):
26
- ...
1
+ from typing import List, Dict
2
+
3
+
4
+ class GraylogApiResult:
5
+ def __init__(self, status_code: int, message: str = "", data: List[Dict] = None):
6
+ """Result returned from low-level RestAdapter
7
+
8
+ :param status_code: Standard HTTP Status code
9
+ :param message: Human readable result
10
+ :param data: Python List of Dictionaries (or maybe just a single Dictionary on error)
11
+ """
12
+ self.status_code = int(status_code)
13
+ self.message = str(message)
14
+ self.data = data if data else []
15
+
16
+
17
+ # TODO make this a proper class
18
+ class GraylogTimerange:
19
+ # Example relative:
20
+ # { "type": "relative", "range": 300 }
21
+ # Example absolute:
22
+ # { "type": "absolute", "from": "2023-01-01T00:00:00.000Z", "to": "2023-01-02T00:00:00.000Z" }
23
+ def relative(range: int):
24
+ ...
25
+ def absolute(from_time: str, to_time: str):
26
+ ...
@@ -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
@@ -0,0 +1,7 @@
1
+ from ...rest_adapter import RestAdapter
2
+ from .SecurityApp import SecurityApp
3
+
4
+ class Plugins:
5
+ def __init__(self, rest_adapter: RestAdapter):
6
+ self._rest_adapter = rest_adapter
7
+ self.SecurityApp = SecurityApp(rest_adapter)
@@ -1,2 +1,2 @@
1
- class GraylogApiException(Exception):
2
- pass
1
+ class GraylogApiException(Exception):
2
+ pass
@@ -1,175 +1,180 @@
1
- import logging
2
- from typing import List, Literal, Union, Dict
3
-
4
- from .rest_adapter import RestAdapter
5
- from .data_structures import GraylogApiResult
6
-
7
-
8
- class GraylogAPI:
9
- def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
10
- """GraylogAPI client
11
-
12
- :param host: The hostname of the Graylog API endpoint plus the protocol. Example: https://graylog.com/api
13
- :param api_key: An API Key to authenticate with Graylog
14
- :param ssl_verify: Enables or Disables TLS Certificate verification. For a custom certificate, this value must a path pointing to the certificate, defaults to True
15
- :param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
16
- """
17
- self._rest_adapter = RestAdapter(host=host, api_key=api_key, ssl_verify=ssl_verify, logger=logger)
18
-
19
- # The endpoints grouped like they are in the api-browser
20
- # /authz Authorization
21
- def get_auth_grants_overview(self) -> GraylogApiResult:
22
- overview = self._rest_adapter.get("authz/grants-overview")
23
- return overview
24
-
25
- def get_auth_roles(self, parameters: Dict = None) -> GraylogApiResult:
26
- roles = self._rest_adapter.get("authz/roles", parameters)
27
- return roles
28
-
29
- def get_auth_roles_of_user(self, username: str, parameters: Dict = None) -> GraylogApiResult:
30
- roles = self._rest_adapter.get(f"authz/roles/user/{username}", parameters)
31
- return roles
32
-
33
- def get_auth_role_by_id(self, role_id: str) -> GraylogApiResult:
34
- roles = self._rest_adapter.get(f"authz/roles/{role_id}")
35
- return roles
36
-
37
- def get_auth_assignees_of_role(self, role_id: str, parameters: Dict = None) -> GraylogApiResult:
38
- assignees = self._rest_adapter.get(f"authz/roles/{role_id}/assignees", parameters)
39
- return assignees
40
-
41
- # /ca
42
- def get_ca(self) -> GraylogApiResult:
43
- result = self._rest_adapter.get("ca")
44
- return result
45
-
46
- # /certificates
47
- def get_certificates(self) -> GraylogApiResult:
48
- result = self._rest_adapter.get("certificates")
49
- return result
50
-
51
- # /cluster
52
- def get_cluster(self) -> GraylogApiResult:
53
- result = self._rest_adapter.get("cluster")
54
- return result
55
-
56
- def get_processbufferdump(self, node_id: str = "") -> GraylogApiResult:
57
- result = self._rest_adapter.get(f"cluster/{node_id}/processbufferdump".replace("//", "/"))
58
- return result
59
-
60
- def get_jvminfo(self, node_id: str) -> GraylogApiResult:
61
- result = self._rest_adapter.get(f"cluster/{node_id}/jvm")
62
- return result
63
-
64
- # /contentstream
65
- # /dashboards
66
- # /datanodes
67
- # /datanode
68
- # /api-docs API-Documentation
69
- # /enterprise
70
- # /entitylists
71
- # /entity_scopes
72
- # /entity_suggestions
73
- # /events
74
- # /system/inputs/{inputId}/extractors Extractors
75
- # /favorites
76
- # /views/fields FieldTypes
77
- # /system/indexer Indexer
78
- # /search/universal/absolute Legacy Message Search
79
- # /messages
80
- # /migration
81
- # /system/pipelines Pipelines
82
- # /plugins
83
- # /remote-reindex-migration
84
- # /roles
85
- # /views/search Search
86
- # /search Search Options (Decorators, Export, Functions etc.)
87
- def post_search_query(self, query: str, start: int, timerange: dict, streams: list[str], stream_categories: list[str] = None,\
88
- sort: str = None, sort_order: Literal["Ascending", "Descending"] = None,\
89
- fields: list[str] = None, size: int = 50) -> GraylogApiResult:
90
- """start replaces the "from" parameter in the API"""
91
- data = {
92
- "query": query,
93
- "from": str(start),
94
- "timerange": timerange,
95
- "streams": streams,
96
- "stream_categories": stream_categories,
97
- "sort": sort,
98
- "sort_order": sort_order,
99
- "fields": fields,
100
- "size": str(size),
101
- }
102
- result = self._rest_adapter.post("search/messages", data=data)
103
- return result
104
-
105
- # /views/searchjobs Searchjobs
106
- # /sidecars
107
- # /sidecar
108
- # /startpage
109
- # /system/inputs/{inputId}/staticfields Staticfields
110
- # /streams
111
- def get_streams(self) -> GraylogApiResult:
112
- result = self._rest_adapter.get("streams")
113
- return result
114
-
115
- # /system Main System endpoints
116
- # /telemetry
117
- # /token_usage
118
- # /users
119
- def create_user(self, username: str, first_name: str, last_name: str, email: str, password: str, permissions: List[str], data: Dict = None) -> GraylogApiResult:
120
- user_data = {
121
- "username": username,
122
- "first_name": first_name,
123
- "last_name": last_name,
124
- "email": email,
125
- "password": password,
126
- }
127
- if permissions:
128
- user_data["permissions"] = permissions
129
- if data:
130
- user_data.update(data)
131
- result = self._rest_adapter.post("users", data=user_data)
132
- return result
133
-
134
- def get_users(self, parameters: Dict = None) -> GraylogApiResult:
135
- result = self._rest_adapter.get("users", parameters)
136
- return result
137
-
138
- def get_users_paginated(self, parameters: Dict = None) -> GraylogApiResult:
139
- result = self._rest_adapter.get("users/paginated", parameters)
140
- return result
141
-
142
- def get_user_by_id(self, user_id: str) -> GraylogApiResult:
143
- result = self._rest_adapter.get(f"users/id/{user_id}")
144
- return result
145
-
146
- def get_user_by_username(self, username: str) -> GraylogApiResult:
147
- result = self._rest_adapter.get(f"users/{username}")
148
- return result
149
-
150
- def get_user_tokens_by_id(self, user_id: str) -> GraylogApiResult:
151
- result = self._rest_adapter.get(f"users/{user_id}/tokens")
152
- return result
153
-
154
- def delete_user_by_id(self, user_id: str) -> GraylogApiResult:
155
- result = self._rest_adapter.delete(f"users/id/{user_id}")
156
- return result
157
-
158
- def delete_user_by_username(self, username: str) -> GraylogApiResult:
159
- result = self._rest_adapter.delete(f"users/{username}")
160
- return result
161
-
162
- def change_user_status(self, user_id: str, status: str) -> GraylogApiResult:
163
- if status not in ["enabled", "disabled", "deleted"]:
164
- raise ValueError(f"Status must be either 'enabled', 'disabled' or 'deleted' but was: {status}")
165
- result = self._rest_adapter.put(f"users/{user_id}/status/{status}")
166
- return result
167
-
168
- # /views Main Views Endpoints
169
- def get_views(self, parameters: Dict = None) -> GraylogApiResult:
170
- result = self._rest_adapter.get("views", parameters)
171
- return result
172
-
173
- def get_view_by_id(self, view_id: str) -> GraylogApiResult:
174
- result = self._rest_adapter.get(f"views/{view_id}")
175
- return result
1
+ import logging
2
+ from datetime import datetime
3
+ from typing import List, Literal, Union, Dict
4
+
5
+ from .rest_adapter import RestAdapter
6
+ from .data_structures import GraylogApiResult
7
+ from .endpoints.authz import Endpoint_AUTH
8
+ from .endpoints.plugins import Plugins
9
+
10
+
11
+ class GraylogAPI:
12
+ def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
13
+ """GraylogAPI client
14
+
15
+ :param host: The hostname of the Graylog API endpoint plus the protocol. Example: https://graylog.com/api
16
+ :param api_key: An API Key to authenticate with Graylog
17
+ :param ssl_verify: Enables or Disables TLS Certificate verification. For a custom certificate, this value must a path pointing to the certificate, defaults to True
18
+ :param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
19
+ """
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)
23
+
24
+ # The endpoints grouped like they are in the api-browser
25
+ # /authz Authorization
26
+ def get_auth_grants_overview(self) -> GraylogApiResult:
27
+ overview = self.authz.get_grants_overview()
28
+ return overview
29
+
30
+ def get_auth_roles(self, parameters: Dict = None) -> GraylogApiResult:
31
+ roles = self.authz.get_roles(parameters)
32
+ return roles
33
+
34
+ def get_auth_roles_of_user(self, username: str, parameters: Dict = None) -> GraylogApiResult:
35
+ roles = self.authz.get_roles_of_user(username, parameters)
36
+ return roles
37
+
38
+ def get_auth_role_by_id(self, role_id: str) -> GraylogApiResult:
39
+ roles = self.authz.get_role_by_id(role_id)
40
+ return roles
41
+
42
+ def get_auth_assignees_of_role(self, role_id: str, parameters: Dict = None) -> GraylogApiResult:
43
+ assignees = self.authz.get_assignees_of_role(role_id, parameters)
44
+ return assignees
45
+
46
+ # /ca
47
+ def get_ca(self) -> GraylogApiResult:
48
+ result = self._rest_adapter.get("ca")
49
+ return result
50
+
51
+ # /certificates
52
+ def get_certificates(self) -> GraylogApiResult:
53
+ result = self._rest_adapter.get("certificates")
54
+ return result
55
+
56
+ # /cluster
57
+ def get_cluster(self) -> GraylogApiResult:
58
+ result = self._rest_adapter.get("cluster")
59
+ return result
60
+
61
+ def get_processbufferdump(self, node_id: str = "") -> GraylogApiResult:
62
+ result = self._rest_adapter.get(f"cluster/{node_id}/processbufferdump".replace("//", "/"))
63
+ return result
64
+
65
+ def get_jvminfo(self, node_id: str) -> GraylogApiResult:
66
+ result = self._rest_adapter.get(f"cluster/{node_id}/jvm")
67
+ return result
68
+
69
+ # /contentstream
70
+ # /dashboards
71
+ # /datanodes
72
+ # /datanode
73
+ # /api-docs API-Documentation
74
+ # /enterprise
75
+ # /entitylists
76
+ # /entity_scopes
77
+ # /entity_suggestions
78
+ # /events
79
+ # /system/inputs/{inputId}/extractors Extractors
80
+ # /favorites
81
+ # /views/fields FieldTypes
82
+ # /system/indexer Indexer
83
+ # /search/universal/absolute Legacy Message Search
84
+ # /messages
85
+ # /migration
86
+ # /system/pipelines Pipelines
87
+ # /plugins
88
+ # /remote-reindex-migration
89
+ # /roles
90
+ # /views/search Search
91
+ # /search Search Options (Decorators, Export, Functions etc.)
92
+ def post_search_query(self, query: str, start: int, timerange: dict, streams: list[str], stream_categories: list[str] = None,\
93
+ sort: str = None, sort_order: Literal["Ascending", "Descending"] = None,\
94
+ fields: list[str] = None, size: int = 50) -> GraylogApiResult:
95
+ """start replaces the "from" parameter in the API"""
96
+ data = {
97
+ "query": query,
98
+ "from": str(start),
99
+ "timerange": timerange,
100
+ "streams": streams,
101
+ "stream_categories": stream_categories,
102
+ "sort": sort,
103
+ "sort_order": sort_order,
104
+ "fields": fields,
105
+ "size": str(size),
106
+ }
107
+ result = self._rest_adapter.post("search/messages", data=data)
108
+ return result
109
+
110
+ # /views/searchjobs Searchjobs
111
+ # /sidecars
112
+ # /sidecar
113
+ # /startpage
114
+ # /system/inputs/{inputId}/staticfields Staticfields
115
+ # /streams
116
+ def get_streams(self) -> GraylogApiResult:
117
+ result = self._rest_adapter.get("streams")
118
+ return result
119
+
120
+ # /system Main System endpoints
121
+ # /telemetry
122
+ # /token_usage
123
+ # /users
124
+ def create_user(self, username: str, first_name: str, last_name: str, email: str, password: str, permissions: List[str], data: Dict = None) -> GraylogApiResult:
125
+ user_data = {
126
+ "username": username,
127
+ "first_name": first_name,
128
+ "last_name": last_name,
129
+ "email": email,
130
+ "password": password,
131
+ }
132
+ if permissions:
133
+ user_data["permissions"] = permissions
134
+ if data:
135
+ user_data.update(data)
136
+ result = self._rest_adapter.post("users", data=user_data)
137
+ return result
138
+
139
+ def get_users(self, parameters: Dict = None) -> GraylogApiResult:
140
+ result = self._rest_adapter.get("users", parameters)
141
+ return result
142
+
143
+ def get_users_paginated(self, parameters: Dict = None) -> GraylogApiResult:
144
+ result = self._rest_adapter.get("users/paginated", parameters)
145
+ return result
146
+
147
+ def get_user_by_id(self, user_id: str) -> GraylogApiResult:
148
+ result = self._rest_adapter.get(f"users/id/{user_id}")
149
+ return result
150
+
151
+ def get_user_by_username(self, username: str) -> GraylogApiResult:
152
+ result = self._rest_adapter.get(f"users/{username}")
153
+ return result
154
+
155
+ def get_user_tokens_by_id(self, user_id: str) -> GraylogApiResult:
156
+ result = self._rest_adapter.get(f"users/{user_id}/tokens")
157
+ return result
158
+
159
+ def delete_user_by_id(self, user_id: str) -> GraylogApiResult:
160
+ result = self._rest_adapter.delete(f"users/id/{user_id}")
161
+ return result
162
+
163
+ def delete_user_by_username(self, username: str) -> GraylogApiResult:
164
+ result = self._rest_adapter.delete(f"users/{username}")
165
+ return result
166
+
167
+ def change_user_status(self, user_id: str, status: str) -> GraylogApiResult:
168
+ if status not in ["enabled", "disabled", "deleted"]:
169
+ raise ValueError(f"Status must be either 'enabled', 'disabled' or 'deleted' but was: {status}")
170
+ result = self._rest_adapter.put(f"users/{user_id}/status/{status}")
171
+ return result
172
+
173
+ # /views Main Views Endpoints
174
+ def get_views(self, parameters: Dict = None) -> GraylogApiResult:
175
+ result = self._rest_adapter.get("views", parameters)
176
+ return result
177
+
178
+ def get_view_by_id(self, view_id: str) -> GraylogApiResult:
179
+ result = self._rest_adapter.get(f"views/{view_id}")
180
+ return result
@@ -1,100 +1,100 @@
1
- import requests
2
- import logging
3
- from typing import Dict, Union
4
-
5
- from .exceptions import GraylogApiException
6
- from .data_structures import GraylogApiResult
7
-
8
-
9
- class RestAdapter:
10
- def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
11
- """Constructor for RestAdapter
12
-
13
- :param host: The hostname of the Graylog API endpoint plus the protocol. Example: https://graylog.com/api
14
- :param api_key: An API Key to authenticate with Graylog
15
- :param ssl_verify: Enables or Disables TLS Certificate verification. For a custom certificate, this value must a path pointing to the certificate, defaults to True
16
- :param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
17
- """
18
- self._logger = logger or logging.getLogger(__name__)
19
- self.host = host + "/"
20
- self._api_key = api_key
21
- self._ssl_verify = ssl_verify
22
- self._session = requests.Session()
23
- self._session.headers.update({"Accept": "application/json", "X-Requested-By": "python-graylog-api-client"})
24
- self._session.auth = (self._api_key, "token") # Graylog uses the format Basic Auth: "<API-token>:token"
25
- if not ssl_verify:
26
- requests.packages.urllib3.disable_warnings()
27
-
28
- def _do(self, method: str, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
29
- """Run the HTTP Requests.
30
-
31
- :param method: The HTTP Method that this request will use.
32
- :param endpoint: The API endpoint that this request goes to. Example: /streams.
33
- :param parameters: Dictionary to be sent in the query string of the Request.
34
- :param data: Dictionary to be sent in the body of the Request.
35
- :raises GraylogApiException: Generic Exception for Errors caused within the Adapter.
36
- :return: An object containing the Result of the API request.
37
- """
38
- url = self.host + endpoint
39
- log_line_pre = f"method={method}, url={url}, parameters={parameters}"
40
- log_line_post = ', '.join((log_line_pre, "success={}, status_code={}, message={}"))
41
- try:
42
- self._logger.debug(msg=log_line_pre)
43
- response = self._session.request(method=method, url=url, verify=self._ssl_verify, params=parameters, json=data)
44
- except requests.exceptions.RequestException as e:
45
- self._logger.error(msg=str(e))
46
- raise GraylogApiException("Invalid API Response") from e
47
- try:
48
- if response.content != b'':
49
- data_out = response.json()
50
- else:
51
- data_out = None
52
- except (ValueError, requests.exceptions.JSONDecodeError) as e:
53
- self._logger.error(msg=log_line_post.format(False, None, e))
54
- raise GraylogApiException("Bad JSON in response") from e
55
- is_success = 299 >= response.status_code >= 200 # OK
56
- log_line = log_line_post.format(is_success, response.status_code, response.reason)
57
- if is_success:
58
- self._logger.debug(msg=log_line)
59
- return GraylogApiResult(status_code=response.status_code, message=response.reason, data=data_out)
60
- self._logger.error(msg=log_line)
61
- raise GraylogApiException(f"{response.status_code}: {response.reason}")
62
-
63
- def get(self, endpoint: str, parameters: Dict = None) -> GraylogApiResult:
64
- """Send a GET request to the API.
65
-
66
- :param endpoint: The API endpoint that this request goes to. Example: /streams.
67
- :param parameters: Dictionary to be sent in the query string of the Request.
68
- :return: An object containing the Result of the API request.
69
- """
70
- return self._do(method="GET", endpoint=endpoint, parameters=parameters)
71
-
72
- def post(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
73
- """Send a POST request to the API.
74
-
75
- :param endpoint: The API endpoint that this request goes to. Example: /streams.
76
- :param parameters: Dictionary to be sent in the query string of the Request.
77
- :param data: Dictionary to be sent in the body of the Request.
78
- :return: An object containing the Result of the API request.
79
- """
80
- return self._do(method="POST", endpoint=endpoint, parameters=parameters, data=data)
81
-
82
- def delete(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
83
- """Send a DELETE request to the API.
84
-
85
- :param endpoint: The API endpoint that this request goes to. Example: /streams.
86
- :param parameters: Dictionary to be sent in the query string of the Request.
87
- :param data: Dictionary to be sent in the body of the Request.
88
- :return: An object containing the Result of the API request.
89
- """
90
- return self._do(method="DELETE", endpoint=endpoint, parameters=parameters, data=data)
91
-
92
- def put(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
93
- """Send a PUT request to the API.
94
-
95
- :param endpoint: The API endpoint that this request goes to. Example: /streams.
96
- :param parameters: Dictionary to be sent in the query string of the Request.
97
- :param data: Dictionary to be sent in the body of the Request.
98
- :return: An object containing the Result of the API request.
99
- """
100
- return self._do(method="PUT", endpoint=endpoint, parameters=parameters, data=data)
1
+ import requests
2
+ import logging
3
+ from typing import Dict, Union
4
+
5
+ from .exceptions import GraylogApiException
6
+ from .data_structures import GraylogApiResult
7
+
8
+
9
+ class RestAdapter:
10
+ def __init__(self, host: str, api_key: str, ssl_verify: Union[str, bool] = True, logger: logging.Logger = None):
11
+ """Constructor for RestAdapter
12
+
13
+ :param host: The hostname of the Graylog API endpoint plus the protocol. Example: https://graylog.com/api
14
+ :param api_key: An API Key to authenticate with Graylog
15
+ :param ssl_verify: Enables or Disables TLS Certificate verification. For a custom certificate, this value must a path pointing to the certificate, defaults to True
16
+ :param logger: The logger being used by the Adapter, will use a new one if none is given, defaults to None
17
+ """
18
+ self._logger = logger or logging.getLogger(__name__)
19
+ self.host = host + "/"
20
+ self._api_key = api_key
21
+ self._ssl_verify = ssl_verify
22
+ self._session = requests.Session()
23
+ self._session.headers.update({"Accept": "application/json", "X-Requested-By": "python-graylog-api-client"})
24
+ self._session.auth = (self._api_key, "token") # Graylog uses the format Basic Auth: "<API-token>:token"
25
+ if not ssl_verify:
26
+ requests.packages.urllib3.disable_warnings()
27
+
28
+ def _do(self, method: str, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
29
+ """Run the HTTP Requests.
30
+
31
+ :param method: The HTTP Method that this request will use.
32
+ :param endpoint: The API endpoint that this request goes to. Example: /streams.
33
+ :param parameters: Dictionary to be sent in the query string of the Request.
34
+ :param data: Dictionary to be sent in the body of the Request.
35
+ :raises GraylogApiException: Generic Exception for Errors caused within the Adapter.
36
+ :return: An object containing the Result of the API request.
37
+ """
38
+ url = self.host + endpoint
39
+ log_line_pre = f"method={method}, url={url}, parameters={parameters}"
40
+ log_line_post = "{}, success={}, status_code={}, message={}"
41
+ try:
42
+ self._logger.debug(msg=log_line_pre)
43
+ response = self._session.request(method=method, url=url, verify=self._ssl_verify, params=parameters, json=data)
44
+ except requests.exceptions.RequestException as e:
45
+ self._logger.error(msg=str(e))
46
+ raise GraylogApiException("Invalid API Response") from e
47
+ try:
48
+ if response.content != b'':
49
+ data_out = response.json()
50
+ else:
51
+ data_out = None
52
+ except (ValueError, requests.exceptions.JSONDecodeError) as e:
53
+ self._logger.error(msg=log_line_post.format(log_line_pre, False, None, e))
54
+ raise GraylogApiException("Bad JSON in response") from e
55
+ is_success = 299 >= response.status_code >= 200 # OK
56
+ log_line = log_line_post.format(log_line_pre, is_success, response.status_code, response.reason)
57
+ if is_success:
58
+ self._logger.debug(msg=log_line)
59
+ return GraylogApiResult(status_code=response.status_code, message=response.reason, data=data_out)
60
+ self._logger.error(msg=log_line)
61
+ raise GraylogApiException(f"{response.status_code}: {response.reason}")
62
+
63
+ def get(self, endpoint: str, parameters: Dict = None) -> GraylogApiResult:
64
+ """Send a GET request to the API.
65
+
66
+ :param endpoint: The API endpoint that this request goes to. Example: /streams.
67
+ :param parameters: Dictionary to be sent in the query string of the Request.
68
+ :return: An object containing the Result of the API request.
69
+ """
70
+ return self._do(method="GET", endpoint=endpoint, parameters=parameters)
71
+
72
+ def post(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
73
+ """Send a POST request to the API.
74
+
75
+ :param endpoint: The API endpoint that this request goes to. Example: /streams.
76
+ :param parameters: Dictionary to be sent in the query string of the Request.
77
+ :param data: Dictionary to be sent in the body of the Request.
78
+ :return: An object containing the Result of the API request.
79
+ """
80
+ return self._do(method="POST", endpoint=endpoint, parameters=parameters, data=data)
81
+
82
+ def delete(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
83
+ """Send a DELETE request to the API.
84
+
85
+ :param endpoint: The API endpoint that this request goes to. Example: /streams.
86
+ :param parameters: Dictionary to be sent in the query string of the Request.
87
+ :param data: Dictionary to be sent in the body of the Request.
88
+ :return: An object containing the Result of the API request.
89
+ """
90
+ return self._do(method="DELETE", endpoint=endpoint, parameters=parameters, data=data)
91
+
92
+ def put(self, endpoint: str, parameters: Dict = None, data: Dict = None) -> GraylogApiResult:
93
+ """Send a PUT request to the API.
94
+
95
+ :param endpoint: The API endpoint that this request goes to. Example: /streams.
96
+ :param parameters: Dictionary to be sent in the query string of the Request.
97
+ :param data: Dictionary to be sent in the body of the Request.
98
+ :return: An object containing the Result of the API request.
99
+ """
100
+ return self._do(method="PUT", endpoint=endpoint, parameters=parameters, data=data)