Skip to content

Commit a171df7

Browse files
authored
Adding security API (#63)
1 parent 8155b95 commit a171df7

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

arangoasync/database.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@
3838
PermissionListError,
3939
PermissionResetError,
4040
PermissionUpdateError,
41+
ServerEncryptionError,
4142
ServerStatusError,
43+
ServerTLSError,
44+
ServerTLSReloadError,
4245
ServerVersionError,
4346
TaskCreateError,
4447
TaskDeleteError,
@@ -2072,6 +2075,81 @@ def response_handler(resp: Response) -> Json:
20722075

20732076
return await self._executor.execute(request, response_handler)
20742077

2078+
async def tls(self) -> Result[Json]:
2079+
"""Return TLS data (keyfile, clientCA).
2080+
2081+
This API requires authentication.
2082+
2083+
Returns:
2084+
dict: dict containing the following components:
2085+
- keyfile: Information about the key file.
2086+
- clientCA: Information about the Certificate Authority (CA) for client certificate verification.
2087+
2088+
Raises:
2089+
ServerTLSError: If the operation fails.
2090+
2091+
References:
2092+
- `get-the-tls-data <https://docs.arangodb.com/stable/develop/http-api/security/#get-the-tls-data>`__
2093+
""" # noqa: E501
2094+
request = Request(method=Method.GET, endpoint="/_admin/server/tls")
2095+
2096+
def response_handler(resp: Response) -> Json:
2097+
if not resp.is_success:
2098+
raise ServerTLSError(resp, request)
2099+
result: Json = self.deserializer.loads(resp.raw_body)["result"]
2100+
return result
2101+
2102+
return await self._executor.execute(request, response_handler)
2103+
2104+
async def reload_tls(self) -> Result[Json]:
2105+
"""Reload TLS data (keyfile, clientCA).
2106+
2107+
This is a protected API and can only be executed with superuser rights.
2108+
2109+
Returns:
2110+
dict: New TLS data.
2111+
2112+
Raises:
2113+
ServerTLSReloadError: If the operation fails.
2114+
2115+
References:
2116+
- `reload-the-tls-data <https://docs.arangodb.com/stable/develop/http-api/security/#reload-the-tls-data>`__
2117+
""" # noqa: E501
2118+
request = Request(method=Method.POST, endpoint="/_admin/server/tls")
2119+
2120+
def response_handler(resp: Response) -> Json:
2121+
if not resp.is_success:
2122+
raise ServerTLSReloadError(resp, request)
2123+
result: Json = self.deserializer.loads(resp.raw_body)["result"]
2124+
return result
2125+
2126+
return await self._executor.execute(request, response_handler)
2127+
2128+
async def encryption(self) -> Result[Json]:
2129+
"""Rotate the user-supplied keys for encryption.
2130+
2131+
This is a protected API and can only be executed with superuser rights.
2132+
This API is not available on Coordinator nodes.
2133+
2134+
Returns:
2135+
dict: Encryption keys.
2136+
2137+
Raises:
2138+
ServerEncryptionError: If the operation fails.
2139+
2140+
References:
2141+
- `rotate-the-encryption-keys <https://docs.arangodb.com/stable/develop/http-api/security/#rotate-the-encryption-keys>`__
2142+
""" # noqa: E501
2143+
request = Request(method=Method.POST, endpoint="/_admin/server/encryption")
2144+
2145+
def response_handler(resp: Response) -> Json:
2146+
if not resp.is_success:
2147+
raise ServerEncryptionError(resp, request)
2148+
result: Json = self.deserializer.loads(resp.raw_body)["result"]
2149+
return result
2150+
2151+
return await self._executor.execute(request, response_handler)
2152+
20752153
async def list_transactions(self) -> Result[Jsons]:
20762154
"""List all currently running stream transactions.
20772155

arangoasync/exceptions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ class SerializationError(ArangoClientError):
435435
"""Failed to serialize the request."""
436436

437437

438+
class ServerEncryptionError(ArangoServerError):
439+
"""Failed to reload user-defined encryption keys."""
440+
441+
438442
class ServerConnectionError(ArangoServerError):
439443
"""Failed to connect to ArangoDB server."""
440444

@@ -443,6 +447,14 @@ class ServerStatusError(ArangoServerError):
443447
"""Failed to retrieve server status."""
444448

445449

450+
class ServerTLSError(ArangoServerError):
451+
"""Failed to retrieve TLS data."""
452+
453+
454+
class ServerTLSReloadError(ArangoServerError):
455+
"""Failed to reload TLS."""
456+
457+
446458
class ServerVersionError(ArangoServerError):
447459
"""Failed to retrieve server version."""
448460

docs/certificates.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,25 @@ Use a client certificate chain
108108

109109
If you want to have fine-grained control over the HTTP connection, you should define
110110
your HTTP client as described in the :ref:`HTTP` section.
111+
112+
Security features
113+
=================
114+
115+
See the `ArangoDB Manual`_ for more information on security features.
116+
117+
**Example:**
118+
119+
.. code-block:: python
120+
121+
async with ArangoClient(hosts=url) as client:
122+
db = await client.db(
123+
sys_db_name, auth_method="superuser", token=token, verify=True
124+
)
125+
126+
# Get TLS data
127+
tls = await db.tls()
128+
129+
# Reload TLS data
130+
tls = await db.reload_tls()
131+
132+
.. _ArangoDB Manual: https://docs.arangodb.com/stable/develop/http-api/security/

docs/migration.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Coming from python-arango
22
-------------------------
33

44
Generally, migrating from `python-arango`_ should be a smooth transition. For the most part, the API is similar,
5-
but there are a few things to note._
5+
but there are a few things to note.
66

77
Helpers
88
=======

tests/test_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from arangoasync.auth import JwtToken
44
from arangoasync.client import ArangoClient
55
from arangoasync.compression import DefaultCompressionManager
6+
from arangoasync.exceptions import ServerEncryptionError
67
from arangoasync.http import DefaultHTTPClient
78
from arangoasync.resolver import DefaultHostResolver, RoundRobinHostResolver
89
from arangoasync.version import __version__
@@ -131,6 +132,19 @@ async def test_client_jwt_superuser_auth(
131132
await db.jwt_secrets()
132133
await db.reload_jwt_secrets()
133134

135+
# Get TLS data
136+
tls = await db.tls()
137+
assert isinstance(tls, dict)
138+
139+
# Reload TLS data
140+
tls = await db.reload_tls()
141+
assert isinstance(tls, dict)
142+
143+
# Rotate
144+
with pytest.raises(ServerEncryptionError):
145+
# Not allowed on coordinators
146+
await db.encryption()
147+
134148
# token missing
135149
async with ArangoClient(hosts=url) as client:
136150
with pytest.raises(ValueError):

0 commit comments

Comments
 (0)