Skip to content

Commit faf3a66

Browse files
authored
fix(GitHub Integration): Fix JWT generation for GitHub API calls (#6983)
1 parent 332df52 commit faf3a66

4 files changed

Lines changed: 72 additions & 34 deletions

File tree

api/integrations/github/client.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,9 @@ def build_request_headers(
5050
}
5151

5252

53-
# TODO: Add test coverage for this function
54-
def generate_token(installation_id: str, app_id: int) -> str: # pragma: no cover
53+
def generate_token(installation_id: str, app_id: int) -> str:
5554
auth: Auth.AppInstallationAuth = Auth.AppAuth(
56-
app_id=int(app_id), private_key=settings.GITHUB_PEM
55+
app_id=str(app_id), private_key=settings.GITHUB_PEM
5756
).get_installation_auth(
5857
installation_id=int(installation_id),
5958
token_permissions=None,
@@ -63,10 +62,9 @@ def generate_token(installation_id: str, app_id: int) -> str: # pragma: no cove
6362
return token
6463

6564

66-
# TODO: Add test coverage for this function
67-
def generate_jwt_token(app_id: int) -> str: # pragma: no cover
65+
def generate_jwt_token(app_id: int) -> str:
6866
github_auth: Auth.AppAuth = Auth.AppAuth(
69-
app_id=app_id,
67+
app_id=str(app_id),
7068
private_key=settings.GITHUB_PEM,
7169
)
7270
token = github_auth.create_jwt()

api/poetry.lock

Lines changed: 7 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pyngo = "~2.4.1"
159159
flagsmith = "^5.1.1"
160160
python-gnupg = "^0.5.1"
161161
django-redis = "^5.4.0"
162-
pygithub = "2.1.1"
162+
pygithub = "~2.8"
163163
hubspot-api-client = "^12.0.0"
164164
djangorestframework-dataclasses = "^1.3.1"
165165
pyotp = "^2.9.0"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import jwt
2+
import pytest
3+
from cryptography.hazmat.primitives import serialization
4+
from cryptography.hazmat.primitives.asymmetric import rsa
5+
from pytest_django.fixtures import SettingsWrapper
6+
from pytest_mock import MockerFixture
7+
8+
from integrations.github.client import generate_jwt_token, generate_token
9+
10+
11+
@pytest.fixture()
12+
def rsa_private_key() -> rsa.RSAPrivateKey:
13+
return rsa.generate_private_key(public_exponent=65537, key_size=2048)
14+
15+
16+
@pytest.fixture(autouse=True)
17+
def github_pem(settings: SettingsWrapper, rsa_private_key: rsa.RSAPrivateKey) -> None:
18+
settings.GITHUB_PEM = rsa_private_key.private_bytes(
19+
encoding=serialization.Encoding.PEM,
20+
format=serialization.PrivateFormat.PKCS8,
21+
encryption_algorithm=serialization.NoEncryption(),
22+
).decode()
23+
24+
25+
def test_generate_token__valid_credentials__returns_installation_token(
26+
mocker: MockerFixture,
27+
) -> None:
28+
# Given
29+
mock_installation_auth = mocker.MagicMock()
30+
mock_installation_auth.token = "installation-token"
31+
mocker.patch(
32+
"integrations.github.client.Auth.AppAuth.get_installation_auth",
33+
return_value=mock_installation_auth,
34+
)
35+
mocker.patch("integrations.github.client.Github")
36+
37+
# When
38+
result = generate_token(installation_id="12345", app_id=67890)
39+
40+
# Then
41+
assert result == "installation-token"
42+
43+
44+
def test_generate_jwt_token__valid_credentials__returns_decodable_jwt(
45+
rsa_private_key: rsa.RSAPrivateKey,
46+
) -> None:
47+
# Given
48+
app_id = 12345
49+
50+
# When
51+
token = generate_jwt_token(app_id=app_id)
52+
53+
# Then
54+
decoded = jwt.decode(
55+
token,
56+
rsa_private_key.public_key(),
57+
algorithms=["RS256"],
58+
options={"verify_exp": False},
59+
)
60+
assert decoded["iss"] == str(app_id)

0 commit comments

Comments
 (0)