Skip to content

Commit 04838e3

Browse files
Refactor fixtures into conftest.py (HarryMWinters#17) 🔂
1 parent e1620da commit 04838e3

File tree

2 files changed

+171
-134
lines changed

2 files changed

+171
-134
lines changed

tests/conftest.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import json
2+
import os
3+
import time
4+
import uuid
5+
6+
import jwt
7+
import pytest
8+
from cryptography.hazmat.backends import default_backend
9+
from cryptography.hazmat.primitives import serialization
10+
from cryptography.hazmat.primitives.asymmetric import rsa
11+
12+
FIXTURES_DIRECTORY = os.path.join(os.path.dirname(__file__), "fixtures")
13+
14+
15+
KEY = rsa.generate_private_key(
16+
backend=default_backend(), public_exponent=65537, key_size=2048
17+
)
18+
19+
20+
@pytest.fixture
21+
def oidc_discovery():
22+
with open(FIXTURES_DIRECTORY + "/AuthServerDiscovery.json") as f:
23+
OIDC_DISCOVERY_RESPONSE = json.load(f)
24+
25+
return OIDC_DISCOVERY_RESPONSE
26+
27+
28+
@pytest.fixture
29+
def test_email():
30+
return "AnticipationOfANewLoversArrivalThe@VeryLittleGravitasIndeed"
31+
32+
33+
@pytest.fixture
34+
def key():
35+
# keeping the key global so it isn't regenerated with each fixture use.
36+
return KEY
37+
38+
39+
@pytest.fixture
40+
def private_key(key):
41+
return key.private_bytes(
42+
serialization.Encoding.PEM,
43+
serialization.PrivateFormat.PKCS8,
44+
serialization.NoEncryption(),
45+
).decode("UTF-8")
46+
47+
48+
@pytest.fixture
49+
def public_key(key):
50+
return (
51+
key.public_key()
52+
.public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.PKCS1)
53+
.decode("UTF-8")
54+
)
55+
56+
57+
@pytest.fixture
58+
def config_w_aud():
59+
return {
60+
"client_id": "CongenitalOptimist",
61+
"audience": "NeverAgain",
62+
"base_authorization_server_uri": "WhatAreTheCivilianApplications?",
63+
"issuer": "PokeItWithAStick",
64+
"signature_cache_ttl": 6e3,
65+
}
66+
67+
68+
@pytest.fixture
69+
def no_audience_config():
70+
return {
71+
"client_id": "CongenitalOptimist",
72+
"base_authorization_server_uri": "WhatAreTheCivilianApplications?",
73+
"issuer": "PokeItWithAStick",
74+
"signature_cache_ttl": 6e3,
75+
}
76+
77+
78+
@pytest.fixture
79+
def token_with_audience(private_key, config_w_aud, test_email) -> str:
80+
audience: str = str(config_w_aud["audience"])
81+
issuer: str = str(config_w_aud["issuer"])
82+
now = int(time.time())
83+
84+
return jwt.encode(
85+
{
86+
"aud": audience,
87+
"iss": issuer,
88+
"email": test_email,
89+
"name": "SweetAndFullOfGrace",
90+
"preferred_username": "Sweet",
91+
"exp": now + 30,
92+
"auth_time": now,
93+
"sub": "foo",
94+
"ver": "1",
95+
"iat": now,
96+
"jti": str(uuid.uuid4()),
97+
"amr": [],
98+
"idp": "",
99+
"nonce": "",
100+
"at_hash": "",
101+
},
102+
private_key,
103+
algorithm="RS256",
104+
).decode("UTF-8")
105+
106+
107+
@pytest.fixture
108+
def token_without_audience(private_key, no_audience_config, test_email) -> str:
109+
# Make a token where audience is client_id
110+
client_id: str = str(no_audience_config["client_id"])
111+
issuer: str = str(no_audience_config["issuer"])
112+
now = int(time.time())
113+
114+
return jwt.encode(
115+
{
116+
"aud": client_id,
117+
"iss": issuer,
118+
"email": test_email,
119+
"name": "SweetAndFullOfGrace",
120+
"preferred_username": "Sweet",
121+
"exp": now + 30,
122+
"auth_time": now,
123+
"sub": "foo",
124+
"ver": "1",
125+
"iat": now,
126+
"jti": str(uuid.uuid4()),
127+
"amr": [],
128+
"idp": "",
129+
"nonce": "",
130+
"at_hash": "",
131+
},
132+
private_key,
133+
algorithm="RS256",
134+
).decode("UTF-8")
135+
136+
137+
@pytest.fixture
138+
def mock_discovery(oidc_discovery, public_key):
139+
class functions:
140+
auth_server = lambda **_: oidc_discovery
141+
public_keys = lambda _: public_key
142+
signing_algos = lambda x: x["id_token_signing_alg_values_supported"]
143+
144+
return lambda *args, **kwargs: functions

tests/test_auth.py

Lines changed: 27 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,42 @@
1-
import json
2-
import os
3-
import time
4-
import uuid
5-
6-
import jwt
7-
from cryptography.hazmat.backends import default_backend
8-
from cryptography.hazmat.primitives import serialization
9-
from cryptography.hazmat.primitives.asymmetric import rsa
10-
111
from fastapi_oidc import auth
2+
from fastapi_oidc.types import IDToken
123

134

14-
class Fixtures:
15-
_fixtures_directory = os.path.join(os.path.dirname(__file__), "fixtures")
16-
17-
with open(_fixtures_directory + "/AuthServerDiscovery.json") as f:
18-
OIDC_DISCOVERY_RESPONSE = json.load(f)
19-
20-
_key = rsa.generate_private_key(
21-
backend=default_backend(), public_exponent=65537, key_size=2048
22-
)
23-
24-
TESTING_PRIVATE_KEY = _key.private_bytes(
25-
serialization.Encoding.PEM,
26-
serialization.PrivateFormat.PKCS8,
27-
serialization.NoEncryption(),
28-
).decode("UTF-8")
29-
30-
TESTING_PUBLIC_KEY = (
31-
_key.public_key()
32-
.public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.PKCS1)
33-
.decode("UTF-8")
34-
)
35-
5+
def test__authenticate_user(
6+
monkeypatch,
7+
mock_discovery,
8+
token_with_audience,
9+
config_w_aud,
10+
test_email,
11+
):
3612

37-
TEST_CONFIG = {
38-
"client_id": "CongenitalOptimist",
39-
"audience": "NeverAgain",
40-
"base_authorization_server_uri": "WhatAreTheCivilianApplications?",
41-
"issuer": "PokeItWithAStick",
42-
"signature_cache_ttl": 6e3,
43-
}
44-
45-
# Test configuration without audience
46-
TEST_CONFIG_NO_AUD = {
47-
"client_id": "CongenitalOptimist",
48-
"base_authorization_server_uri": "WhatAreTheCivilianApplications?",
49-
"issuer": "PokeItWithAStick",
50-
"signature_cache_ttl": 6e3,
51-
}
52-
53-
54-
def _make_token(
55-
email: str,
56-
private_key: str = Fixtures.TESTING_PRIVATE_KEY,
57-
client_id: str = str(TEST_CONFIG["client_id"]),
58-
audience: str = str(TEST_CONFIG["audience"]),
59-
issuer: str = str(TEST_CONFIG["issuer"]),
60-
) -> str:
61-
now = int(time.time())
62-
return jwt.encode(
63-
{
64-
"aud": audience,
65-
"iss": issuer,
66-
"email": email,
67-
"name": "SweetAndFullOfGrace",
68-
"preferred_username": "Sweet",
69-
"exp": now + 30,
70-
"auth_time": now,
71-
"sub": "foo",
72-
"ver": "1",
73-
"iat": now,
74-
"jti": str(uuid.uuid4()),
75-
"amr": [],
76-
"idp": "",
77-
"nonce": "",
78-
"at_hash": "",
79-
},
80-
private_key,
81-
algorithm="RS256",
82-
).decode("UTF-8")
13+
monkeypatch.setattr(auth.discovery, "configure", mock_discovery)
8314

15+
token = token_with_audience
8416

85-
# Make a token where audience is client_id
86-
def _make_token_no_aud(
87-
email: str,
88-
private_key: str = Fixtures.TESTING_PRIVATE_KEY,
89-
client_id: str = str(TEST_CONFIG_NO_AUD["client_id"]),
90-
issuer: str = str(TEST_CONFIG_NO_AUD["issuer"]),
91-
) -> str:
92-
now = int(time.time())
93-
return jwt.encode(
94-
{
95-
"aud": client_id,
96-
"iss": issuer,
97-
"email": email,
98-
"name": "SweetAndFullOfGrace",
99-
"preferred_username": "Sweet",
100-
"exp": now + 30,
101-
"auth_time": now,
102-
"sub": "foo",
103-
"ver": "1",
104-
"iat": now,
105-
"jti": str(uuid.uuid4()),
106-
"amr": [],
107-
"idp": "",
108-
"nonce": "",
109-
"at_hash": "",
110-
},
111-
private_key,
112-
algorithm="RS256",
113-
).decode("UTF-8")
17+
authenticate_user = auth.get_auth(**config_w_aud)
18+
id_token: IDToken = authenticate_user(auth_header=f"Bearer {token}")
11419

20+
assert id_token.email == test_email # nosec
21+
assert id_token.aud == config_w_aud["audience"]
11522

116-
def test__authenticate_user(monkeypatch):
117-
def mock_discovery(*args, **kwargs):
118-
class functions:
119-
auth_server = lambda **_: Fixtures.OIDC_DISCOVERY_RESPONSE
120-
public_keys = lambda _: Fixtures.TESTING_PUBLIC_KEY
121-
signing_algos = lambda x: x["id_token_signing_alg_values_supported"]
12223

123-
return functions
24+
# Ensure that when no audience is supplied, that the audience defaults to client ID
25+
def test__authenticate_user_no_aud(
26+
monkeypatch,
27+
mock_discovery,
28+
token_without_audience,
29+
no_audience_config,
30+
test_email,
31+
):
12432

12533
monkeypatch.setattr(auth.discovery, "configure", mock_discovery)
126-
email = "AnticipationOfANewLoversArrivalThe@VeryLittleGravitasIndeed"
127-
token = _make_token(email=email)
128-
authenticate_user = auth.get_auth(**TEST_CONFIG)
129-
IDToken = authenticate_user(auth_header=f"Bearer {token}")
130-
assert IDToken.email == email # nosec
13134

35+
token = token_without_audience
13236

133-
# Ensure that when no audience is supplied, that the audience defaults to client ID
134-
def test__authenticate_user_no_aud(monkeypatch):
135-
def mock_discovery(*args, **kwargs):
136-
class functions:
137-
auth_server = lambda **_: Fixtures.OIDC_DISCOVERY_RESPONSE
138-
public_keys = lambda _: Fixtures.TESTING_PUBLIC_KEY
139-
signing_algos = lambda x: x["id_token_signing_alg_values_supported"]
37+
authenticate_user = auth.get_auth(**no_audience_config)
14038

141-
return functions
39+
id_token: IDToken = authenticate_user(auth_header=f"Bearer {token}")
14240

143-
monkeypatch.setattr(auth.discovery, "configure", mock_discovery)
144-
email = "AnticipationOfANewLoversArrivalThe@VeryLittleGravitasIndeed"
145-
token = _make_token_no_aud(email=email)
146-
authenticate_user = auth.get_auth(**TEST_CONFIG_NO_AUD)
147-
IDToken = authenticate_user(auth_header=f"Bearer {token}")
148-
assert IDToken.email == email # nosec
149-
assert IDToken.aud == TEST_CONFIG_NO_AUD["client_id"]
41+
assert id_token.email == test_email # nosec
42+
assert id_token.aud == no_audience_config["client_id"]

0 commit comments

Comments
 (0)