Skip to content

Commit 2cfa7ae

Browse files
authored
Add exception handling to reduce the types of data that can be returned & tests (#16)
* Add exception handling to reduce the types of data that can be returned to reduce footguns * Add some basic tests * Add a GH Workflow for running the tests
1 parent 024df03 commit 2cfa7ae

4 files changed

Lines changed: 134 additions & 5 deletions

File tree

.github/workflows/tests.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Run unit tests
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
tests:
8+
runs-on: ubuntu-latest
9+
10+
strategy:
11+
matrix:
12+
python-version:
13+
- "3.8"
14+
- "3.9"
15+
- "3.10"
16+
- "3.11"
17+
- "3.12"
18+
- "3.13"
19+
- "3.14"
20+
21+
steps:
22+
- name: Check out repository
23+
uses: actions/checkout@v6
24+
25+
- name: Set up Python
26+
uses: actions/setup-python@v6
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Run unit tests
31+
run: |
32+
python -m unittest discover -s test -v

pysteamsignin/steamsignin.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,13 @@ def ValidateResults(self, results):
131131
parsedArgs = urlencode(validationArgs).encode("utf-8")
132132
logger.info('Encoded the validation arguments, prepped to send.')
133133

134-
with urllib.request.urlopen(self._provider, parsedArgs, timeout = OPENID_TIMEOUT) as requestData:
135-
responseData = requestData.read().decode('utf-8')
136-
logger.info(f"Sent request to {self._provider}, got back a response.")
134+
try:
135+
with urllib.request.urlopen(self._provider, parsedArgs, timeout = OPENID_TIMEOUT) as requestData:
136+
responseData = requestData.read().decode("utf-8")
137+
logger.info(f"Sent request to {self._provider}, got back a response.")
138+
except Exception as e:
139+
logger.warning(f"Steam OpenID verification failed: {e}")
140+
return False
137141

138142
fields = {}
139143

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="steamsignin",
8-
version="1.1.2",
8+
version="1.1.3",
99
author="TeddiO",
1010
author_email="",
1111
description="OpenID 2.0 sign in for Steam",
@@ -20,6 +20,6 @@
2020
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
2121
"Operating System :: OS Independent",
2222
],
23-
packages=setuptools.find_packages(exclude=('samples')),
23+
packages=setuptools.find_packages(exclude=('samples', '.github')),
2424
python_requires=">=3.6"
2525
)

test/tests.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import unittest
2+
import sys
3+
import os
4+
import logging
5+
from unittest.mock import patch
6+
import urllib.error
7+
import socket
8+
9+
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__))))
10+
from pysteamsignin.steamsignin import SteamSignIn
11+
12+
13+
def valid_results():
14+
steam_id = "12345678901234567"
15+
claimed = f"https://steamcommunity.com/openid/id/{steam_id}"
16+
17+
return {
18+
"openid.assoc_handle": "assoc",
19+
"openid.signed": "claimed_id,identity",
20+
"openid.sig": "sig",
21+
"openid.ns": "http://specs.openid.net/auth/2.0",
22+
"openid.claimed_id": claimed,
23+
"openid.identity": claimed,
24+
}
25+
26+
27+
class FakeSteamResponse:
28+
def __init__(self, body):
29+
self._body = body.encode("utf-8")
30+
31+
def read(self):
32+
return self._body
33+
34+
def __enter__(self):
35+
return self
36+
37+
def __exit__(self, exc_type, exc, tb):
38+
pass
39+
40+
41+
class TestSteamSignIn(unittest.TestCase):
42+
43+
@patch("urllib.request.urlopen")
44+
def test_valid_openid_response_returns_steamid_string(self, mock_urlopen):
45+
mock_urlopen.return_value = FakeSteamResponse("is_valid:true\n")
46+
47+
signin = SteamSignIn()
48+
result = signin.ValidateResults(valid_results())
49+
50+
self.assertEqual(result, "12345678901234567")
51+
52+
@patch("urllib.request.urlopen")
53+
def test_steam_is_valid_false_denies_login(self, mock_urlopen):
54+
mock_urlopen.return_value = FakeSteamResponse("is_valid:false\n")
55+
56+
signin = SteamSignIn()
57+
result = signin.ValidateResults(valid_results())
58+
59+
self.assertFalse(result)
60+
61+
@patch("urllib.request.urlopen", side_effect = socket.timeout("timed out"))
62+
def test_openid_timeout_fails_closed_and_logs_warning(self, mock_urlopen):
63+
with self.assertLogs("pysteamsignin.steamsignin", level = logging.WARNING) as logs:
64+
signin = SteamSignIn()
65+
result = signin.ValidateResults(valid_results())
66+
67+
self.assertFalse(result)
68+
self.assertIn("Steam OpenID verification failed", logs.output[0])
69+
70+
@patch("urllib.request.urlopen", side_effect = urllib.error.URLError("connection failed"))
71+
def test_network_error_fails_closed_and_logs_warning(self, mock_urlopen):
72+
with self.assertLogs("pysteamsignin.steamsignin", level = logging.WARNING) as logs:
73+
signin = SteamSignIn()
74+
result = signin.ValidateResults(valid_results())
75+
76+
self.assertFalse(result)
77+
self.assertIn("Steam OpenID verification failed", logs.output[0])
78+
79+
@patch("urllib.request.urlopen")
80+
def test_claimed_id_identity_mismatch_fails_validation(self, mock_urlopen):
81+
mock_urlopen.return_value = FakeSteamResponse("is_valid:true\n")
82+
83+
results = valid_results()
84+
results["openid.identity"] = "https://steamcommunity.com/openid/id/DIFFERENT"
85+
86+
signin = SteamSignIn()
87+
result = signin.ValidateResults(results)
88+
89+
self.assertFalse(result)
90+
91+
92+
if __name__ == "__main__":
93+
unittest.main()

0 commit comments

Comments
 (0)