From 6ad592b1b6eadf7093215ff3b4bbfe965206f640 Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Fri, 25 Oct 2019 21:01:58 +1100 Subject: [PATCH] Correct header check for 2FA required (#1274) Currently, we were checking for an uppercase header if GitHub was refusing our authentication because we didn't provide a token. Requests returns headers with all lowercase. Fix the const, and add a test for good measure. Fixes #1273 --- github/Consts.py | 2 +- github/Requester.py | 2 +- tests/Exceptions.py | 3 +++ .../ReplayData/SpecificExceptions.test2FARequired.txt | 10 ++++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/ReplayData/SpecificExceptions.test2FARequired.txt diff --git a/github/Consts.py b/github/Consts.py index 650264f3..5fc2aacf 100644 --- a/github/Consts.py +++ b/github/Consts.py @@ -49,7 +49,7 @@ headerRateLimit = "x-ratelimit-limit" headerRateRemaining = "x-ratelimit-remaining" headerRateReset = "x-ratelimit-reset" headerOAuthScopes = "x-oauth-scopes" -headerOTP = "X-GitHub-OTP" +headerOTP = "x-github-otp" defaultMediaType = "application/octet-stream" diff --git a/github/Requester.py b/github/Requester.py index f3152b7f..1ac87768 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -296,7 +296,7 @@ class Requester: if status == 401 and output.get("message") == "Bad credentials": cls = GithubException.BadCredentialsException elif status == 401 and Consts.headerOTP in headers and re.match(r'.*required.*', headers[Consts.headerOTP]): - cls = GithubException.TwoFactorException # pragma no cover (Should be covered) + cls = GithubException.TwoFactorException elif status == 403 and output.get("message").startswith("Missing or invalid User Agent string"): cls = GithubException.BadUserAgentException elif status == 403 and ( diff --git a/tests/Exceptions.py b/tests/Exceptions.py index 9ee5dae4..acd34480 100644 --- a/tests/Exceptions.py +++ b/tests/Exceptions.py @@ -100,6 +100,9 @@ class SpecificExceptions(Framework.TestCase): def testBadCredentials(self): self.assertRaises(github.BadCredentialsException, lambda: github.Github("BadUser", "BadPassword").get_user().login) + def test2FARequired(self): + self.assertRaises(github.TwoFactorException, lambda: github.Github("2fauser", "password").get_user().login) + def testUnknownObject(self): self.assertRaises(github.UnknownObjectException, lambda: self.g.get_user().get_repo("Xxx")) diff --git a/tests/ReplayData/SpecificExceptions.test2FARequired.txt b/tests/ReplayData/SpecificExceptions.test2FARequired.txt new file mode 100644 index 00000000..82f709c9 --- /dev/null +++ b/tests/ReplayData/SpecificExceptions.test2FARequired.txt @@ -0,0 +1,10 @@ +https +GET +api.github.com +None +/user +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +401 +[('date', 'Fri, 25 Oct 2019 07:56:02 GMT'), ('content-type', 'application/json; charset=utf-8'), ('content-length', '160'), ('server', 'GitHub.com'), ('status', '401 Unauthorized'), ('x-github-otp', 'required; app'), ('x-github-media-type', 'github.v3; format=json'), ('x-ratelimit-limit', '60'), ('x-ratelimit-remaining', '58'), ('x-ratelimit-reset', '1571993689'), ('access-control-expose-headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('access-control-allow-origin', '*'), ('strict-transport-security', 'max-age=31536000; includeSubdomains; preload'), ('x-frame-options', 'deny'), ('x-content-type-options', 'nosniff'), ('x-xss-protection', '1; mode=block'), ('referrer-policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('content-security-policy', "default-src 'none'"), ('x-github-request-id', 'B376:7075:202D9C:250D5E:5DB2AA92')] +{"message":"Must specify two-factor authentication OTP code.", "documentation_url": "https://developer.github.com/v3/auth#working-with-two-factor-authentication"}