Skip to content

Commit 44aae44

Browse files
authored
Merge pull request #62 from GitGuardian/ppetit/-/jwt-creation
feat: add create_jwt() endpoint
2 parents c180fea + 25d50bd commit 44aae44

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Added
2+
3+
- Added `GGClient.create_jwt()` method. Only used to interact with HasMySecretLeaked for now.

pygitguardian/client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
DocumentSchema,
2020
HealthCheckResponse,
2121
HoneytokenResponse,
22+
JWTResponse,
23+
JWTService,
2224
MultiScanResult,
2325
QuotaResponse,
2426
ScanResult,
@@ -512,3 +514,27 @@ def read_metadata(self) -> Optional[Detail]:
512514

513515
self.secret_scan_preferences = metadata.secret_scan_preferences
514516
return None
517+
518+
def create_jwt(
519+
self,
520+
jwt_audience: str,
521+
service: JWTService,
522+
extra_headers: Optional[Dict[str, str]] = None,
523+
) -> Union[Detail, JWTResponse]:
524+
"""
525+
Create a JWT token for other GitGuardian services.
526+
:return: Detail or JWT response and status code
527+
"""
528+
529+
resp = self.post(
530+
endpoint="auth/jwt",
531+
data={"audience": jwt_audience, "audience_type": service.value},
532+
extra_headers=extra_headers,
533+
)
534+
obj: Union[Detail, JWTResponse]
535+
if is_ok(resp):
536+
obj = JWTResponse.from_dict(resp.json())
537+
else:
538+
obj = load_detail(resp)
539+
obj.status_code = resp.status_code
540+
return obj

pygitguardian/models.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from dataclasses import dataclass, field
22
from datetime import date, datetime
3+
from enum import Enum
34
from typing import Any, ClassVar, Dict, List, Optional, cast
45
from uuid import UUID
56

@@ -668,3 +669,34 @@ class ServerMetadata(Base, FromDictMixin):
668669
BaseSchema,
669670
marshmallow_dataclass.class_schema(ServerMetadata, base_schema=BaseSchema)(),
670671
)
672+
673+
674+
class JWTResponseSchema(BaseSchema):
675+
token = fields.String(required=True)
676+
677+
@post_load
678+
def make_response(self, data: Dict[str, str], **kwargs: Any) -> "JWTResponse":
679+
return JWTResponse(**data)
680+
681+
682+
class JWTResponse(Base, FromDictMixin):
683+
"""Token to use the HasMySecretLeaked service.
684+
685+
Attributes:
686+
token (str): the JWT token
687+
"""
688+
689+
SCHEMA = JWTResponseSchema()
690+
691+
def __init__(self, token: str, **kwargs: Any) -> None:
692+
super().__init__()
693+
self.token = token
694+
695+
def __repr__(self) -> str:
696+
return self.token
697+
698+
699+
class JWTService(Enum):
700+
"""Enum for the different services GIM can generate a JWT for."""
701+
702+
HMSL = "hmsl"

tests/test_client.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from pygitguardian.models import (
2121
Detail,
2222
HoneytokenResponse,
23+
JWTResponse,
24+
JWTService,
2325
MultiScanResult,
2426
QuotaResponse,
2527
ScanResult,
@@ -736,3 +738,26 @@ def test_create_honeytoken_error(
736738

737739
assert mock_response.call_count == 1
738740
assert isinstance(result, Detail)
741+
742+
743+
@responses.activate
744+
def test_get_wt(
745+
client: GGClient,
746+
):
747+
"""
748+
GIVEN a ggclient
749+
WHEN calling create_jwt
750+
THEN we receive a token
751+
"""
752+
mock_response = responses.post(
753+
url=client._url_from_endpoint("auth/jwt", "v1"),
754+
content_type="application/json",
755+
status=200,
756+
json={"token": "dummy_token"},
757+
)
758+
759+
result = client.create_jwt("dummy_audience", JWTService.HMSL)
760+
761+
assert mock_response.call_count == 1
762+
assert isinstance(result, JWTResponse)
763+
assert result.token == "dummy_token"

0 commit comments

Comments
 (0)