Skip to content

Commit 92de1b0

Browse files
committed
JWT Connection working
1 parent 9626270 commit 92de1b0

File tree

3 files changed

+64
-21
lines changed

3 files changed

+64
-21
lines changed

arangoasync/connection.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ async def ping(self) -> int:
116116
ServerConnectionError: If the response status code is not successful.
117117
"""
118118
request = Request(method=Method.GET, endpoint="/_api/collection")
119+
request.headers = {"abde": "fghi"}
119120
resp = await self.send_request(request)
120121
return resp.status_code
121122

@@ -190,8 +191,7 @@ async def send_request(self, request: Request) -> Response:
190191
class JwtConnection(BaseConnection):
191192
"""Connection to a specific ArangoDB database, using JWT authentication.
192193
193-
Allows for basic authentication to be used (username and password),
194-
together with JWT.
194+
Providing login information (username and password), allows to refresh the JWT.
195195
196196
Args:
197197
sessions (list): List of client sessions.
@@ -221,7 +221,7 @@ def __init__(
221221
self._expire_leeway: int = 0
222222
self._token: Optional[JwtToken] = None
223223
self._auth_header: Optional[str] = None
224-
self.set_token(token)
224+
self.token = token
225225

226226
if self._token is None and self._auth is None:
227227
raise ValueError("Either token or auth must be provided.")
@@ -261,21 +261,31 @@ async def refresh_token(self) -> None:
261261

262262
token = json.loads(resp.raw_body)
263263
try:
264-
self.set_token(JwtToken(token["jwt"]))
264+
self.token = JwtToken(token["jwt"])
265265
except jwt.ExpiredSignatureError as e:
266266
raise JWTRefreshError(
267267
"Failed to refresh the JWT token: got an expired token"
268268
) from e
269269

270-
def set_token(self, value: Optional[JwtToken]) -> None:
270+
@property
271+
def token(self) -> Optional[JwtToken]:
272+
"""Get the JWT token.
273+
274+
Returns:
275+
JwtToken | None: JWT token.
276+
"""
277+
return self._token
278+
279+
@token.setter
280+
def token(self, token: Optional[JwtToken]) -> None:
271281
"""Set the JWT token.
272282
273283
Args:
274-
value (JwtToken | None): JWT token.
284+
token (JwtToken | None): JWT token.
275285
Setting it to None will cause the token to be automatically
276286
refreshed on the next request, if auth information is provided.
277287
"""
278-
self._token = value
288+
self._token = token
279289
self._auth_header = f"bearer {self._token.token}" if self._token else None
280290

281291
async def send_request(self, request: Request) -> Response:
@@ -291,20 +301,20 @@ async def send_request(self, request: Request) -> Response:
291301
ArangoClientError: If an error occurred from the client side.
292302
ArangoServerError: If an error occurred from the server side.
293303
"""
294-
if self._auth_header is not None:
295-
request.headers["authorization"] = self._auth_header
296-
else:
304+
if self._auth_header is None:
297305
await self.refresh_token()
306+
request.headers["authorization"] = self._auth_header
298307

299308
try:
300309
resp = await self.process_request(request)
301310
if (
302-
resp.status_code == 401
311+
resp.status_code == 401 # Unauthorized
303312
and self._token is not None
304313
and self._token.needs_refresh(self._expire_leeway)
305314
):
306315
await self.refresh_token()
307-
return await self.process_request(request)
308-
except ServerConnectionError as e:
316+
return await self.process_request(request) # Retry with new token
317+
except ServerConnectionError:
309318
# TODO modify after refactoring of prep_response, so we can inspect response
310-
raise e
319+
await self.refresh_token()
320+
return await self.process_request(request) # Retry with new token

tests/helpers.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import time
2+
3+
import jwt
4+
5+
6+
def generate_jwt(secret, exp=3600) -> str:
7+
"""Generate and return a JWT token.
8+
9+
Args:
10+
secret (str | bytes): JWT secret.
11+
exp (int): Time to expire in seconds.
12+
13+
Returns:
14+
str: JWT token.
15+
"""
16+
now = int(time.time())
17+
return jwt.encode(
18+
payload={
19+
"iat": now,
20+
"exp": now + exp,
21+
"iss": "arangodb",
22+
"server_id": "client",
23+
},
24+
key=secret,
25+
)

tests/test_connection.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from arangoasync.auth import Auth, JwtToken
5+
from arangoasync.auth import Auth
66
from arangoasync.compression import AcceptEncoding, DefaultCompressionManager
77
from arangoasync.connection import BasicConnection, JwtConnection
88
from arangoasync.exceptions import (
@@ -134,22 +134,30 @@ async def mock_send_request(*args, **kwargs):
134134

135135
@pytest.mark.asyncio
136136
async def test_JwtConnection_ping_success(
137-
client_session, url, sys_db_name, root, password, token
137+
client_session, url, sys_db_name, root, password
138138
):
139139
client = AioHTTPClient()
140140
session = client_session(client, url)
141141
resolver = DefaultHostResolver(1)
142-
token = JwtToken(token)
143142

144-
connection = JwtConnection(
143+
connection1 = JwtConnection(
145144
sessions=[session],
146145
host_resolver=resolver,
147146
http_client=client,
148147
db_name=sys_db_name,
149148
auth=Auth(username=root, password=password),
150-
token=token,
151149
)
150+
assert connection1.db_name == sys_db_name
151+
status_code = await connection1.ping()
152+
assert status_code == 200
152153

153-
assert connection.db_name == sys_db_name
154-
status_code = await connection.ping()
154+
connection2 = JwtConnection(
155+
sessions=[session],
156+
host_resolver=resolver,
157+
http_client=client,
158+
db_name=sys_db_name,
159+
token=connection1.token,
160+
)
161+
assert connection2.db_name == sys_db_name
162+
status_code = await connection2.ping()
155163
assert status_code == 200

0 commit comments

Comments
 (0)