Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2FA (TOTP) support #5567

Merged
merged 121 commits into from
May 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
944c1c8
Add a TOTP secret field to `User` model
sergeykolosov Jul 29, 2018
ba1cff9
Add pyotp package
asfaltboy Jul 28, 2018
7abc62a
Add TOTP primitives
sergeykolosov Jul 29, 2018
558f01d
Add template of OTP verification page
Sparkycz Jul 28, 2018
75b628c
Add presenter/view of two-factor verification (login process)
Sparkycz Jul 29, 2018
d6995ee
warehouse, tests: Auto-format
woodruffw Mar 11, 2019
cb800dc
accounts/views: Remove datadog instrumentation
woodruffw Mar 12, 2019
9bc6597
migrations: Rebase TOTP migration
woodruffw Mar 12, 2019
a1eea98
tests/accounts: Fix some of the views tests
woodruffw Mar 12, 2019
068a68c
accounts/services: Implement has_two_factor
woodruffw Mar 12, 2019
5bb2cbd
warehouse: Support for adding and removing TOTP
woodruffw Mar 12, 2019
79b906f
templates/manage: PoC management UI
woodruffw Mar 12, 2019
5060eb4
tests, warehouse: otp_secret -> otp_value
woodruffw Mar 12, 2019
77db097
warehouse: Replace pyotp with cryptography
woodruffw Mar 13, 2019
367065a
manage/views: Return default resp on error
woodruffw Mar 13, 2019
4071df3
manage/views: Auto-format
woodruffw Mar 13, 2019
7a1198c
tests: Fix OTP, account view tests
woodruffw Mar 13, 2019
6fa8e52
tests: Remove vestigial assert
woodruffw Mar 13, 2019
c64a98b
tests: Fix manage test, add stubs
woodruffw Mar 13, 2019
3fd0954
warehouse/templates: Reindent two-factor.html
woodruffw Mar 13, 2019
deaa824
tests: Fix includeme test
woodruffw Mar 14, 2019
da0dc81
warehouse: Refactor TOTP provisioning
woodruffw Mar 14, 2019
324fa40
tests: Add additional OTP validation tests
woodruffw Mar 15, 2019
569672a
warehouse: Share TOTPValueMixin, clean up template
woodruffw Mar 15, 2019
68e2bd3
warehouse: Add TOTP URI generation to services
woodruffw Mar 15, 2019
7eec935
tests: Add more TOTP management tests
woodruffw Mar 15, 2019
f76db0f
warehouse: Fix totp_provisioned call
woodruffw Mar 15, 2019
3f83971
tests: Formatting
woodruffw Mar 15, 2019
0c76185
warehouse: Remove unused fstrings, add flash
woodruffw Mar 15, 2019
3f5e5a9
tests: More TOTP management tests
woodruffw Mar 15, 2019
be4f60d
warehouse/otp: Refactor, remove window parameter
woodruffw Mar 18, 2019
f720980
tests: More view tests, more stubs
woodruffw Mar 18, 2019
a5c01c6
warehouse: Use TOTP_LENGTH instead of hardcoding
woodruffw Mar 18, 2019
0049010
warehouse: Fix TOTP_LENGTH interpolation
woodruffw Mar 18, 2019
2e8aed4
warehouse: Auto format
woodruffw Mar 18, 2019
6414b5f
warehouse: Removed unused default_view, module nit
woodruffw Mar 20, 2019
25190f0
tests: TOTP management view tests
woodruffw Mar 20, 2019
cd44003
warehouse: Import as otp, fallthrough case
woodruffw Mar 21, 2019
b65480d
tests: Final services, views, forms tests
woodruffw Mar 21, 2019
ea4a777
warehouse/interfaces: Add totp_provisioning_uri
woodruffw Mar 21, 2019
f25560a
warehouse/interfaces: Fix totp_provisioning_uri prototype
woodruffw Mar 21, 2019
c671be4
warehouse: Don't allow provisioned users to see their TOTP secret again
woodruffw Mar 21, 2019
baadaf2
tests, warehouse: Remove non-default encode/decode
woodruffw Mar 22, 2019
6705801
warehouse/accounts/views: Reformat
woodruffw Mar 22, 2019
c6f9e63
Add 2fa help content to help page
nlhkabu Mar 22, 2019
45be781
Improve UI of TOTP authentication page
nlhkabu Mar 22, 2019
4541303
Add copy and format TOTP provision UI
nlhkabu Mar 22, 2019
cb85e97
Improve TOTP enable/disable UI
nlhkabu Mar 22, 2019
bf10974
warehouse, tests: Link up template changes
woodruffw Mar 22, 2019
1f3644d
warehouse: TOTP QR generation
woodruffw Mar 22, 2019
0c51af5
tests: Update flash tests
woodruffw Mar 22, 2019
c7af820
tests: Fix html lint check
offlinemark Mar 27, 2019
82ab3ca
Fix import lint errors
offlinemark Mar 27, 2019
3bc0b2d
Fix misc lint errors
offlinemark Mar 27, 2019
25ee73a
Move totp fields into new table
offlinemark Mar 28, 2019
9032328
Initial porting of code to new db structure
offlinemark Mar 28, 2019
40d492c
Make test_has_two_factor pass
offlinemark Mar 29, 2019
ff7b029
Fix rest of services tests
offlinemark Mar 29, 2019
2cdb7d6
warehouse: OtpInfo -> TwoFactor, integration into flow
woodruffw Apr 1, 2019
14cec33
warehouse: Formatting, testability improvements
woodruffw Apr 2, 2019
4af6f3b
tests: Fix unit tests
woodruffw Apr 2, 2019
bb6b738
warehouse: Update service iface, handle bad userids
woodruffw Apr 2, 2019
39579f0
tests: Add bad user ID -> two factor lookup test
woodruffw Apr 2, 2019
50a8bd2
warehouse: isort import fixes
woodruffw Apr 2, 2019
ed4b07a
warehouse: Stash the TOTP secret in a signed cookie
woodruffw Apr 2, 2019
3c05a98
tests: Rewrite tests to work with signed TOTP cookie
woodruffw Apr 2, 2019
21f3dbc
warehouse: Fix import order
woodruffw Apr 3, 2019
e10d3dd
tests: More tests for new TOTP behavior
woodruffw Apr 3, 2019
a10d707
tests: Autoformatting
woodruffw Apr 3, 2019
4c10f14
warehouse: Delete TOTP provisioning cookie properly
woodruffw Apr 3, 2019
027b6d3
Update warehouse/templates/pages/help.html
nlhkabu Apr 4, 2019
51f5953
Apply suggestions from code review
nlhkabu Apr 4, 2019
d5a9db8
Apply suggestions from code review
webknjaz Apr 4, 2019
fd8002c
warehouse: Parameterize TOTP issuer_name
woodruffw Apr 5, 2019
c4d653c
tests: Update tests to handle issuer_name
woodruffw Apr 5, 2019
bcf3fdc
warehouse: Add noscript fallback for TOTP provisioning
woodruffw Apr 5, 2019
677d572
warehouse: Fix HTML tags
woodruffw Apr 5, 2019
b515d48
warehouse: Remove old migration, DB nits
woodruffw Apr 15, 2019
4495c10
warehouse, dev: Remove TOKEN_TWO_FACTOR_SECRET
woodruffw Apr 15, 2019
ff1f70b
tests: Remove churn change
woodruffw Apr 15, 2019
069798a
warehouse/accounts: Validate TOTP within form
woodruffw Apr 15, 2019
97be700
warehouse: Stash the TOTP secret in session data
woodruffw Apr 15, 2019
3ad09b2
warehouse, dev: Remove old TOTP provisioning cookie
woodruffw Apr 15, 2019
37d88c0
tests: Fix account view tests
woodruffw Apr 15, 2019
fbe289d
tests: Fix config tests
woodruffw Apr 15, 2019
8c14250
warehouse: Avoid unnecessary TOTP secrets on sessions
woodruffw Apr 15, 2019
e795bc3
tests: Fix remaining tests
woodruffw Apr 15, 2019
f591fe9
tests: More coverage
woodruffw Apr 15, 2019
f3db1fa
tests: 100% coverage, remove old imports
woodruffw Apr 16, 2019
7ca9f0a
tests, warehouse: import sorting
woodruffw Apr 16, 2019
a3f1188
tests, warehouse: get_two_factor raises on missing user
woodruffw Apr 16, 2019
a779d21
tests, warehouse: Use login metric tags on 2FA
woodruffw Apr 16, 2019
9cd2ce0
warehouse: Don't cache TOTP provisioning views
woodruffw Apr 16, 2019
fb32820
warehouse: Generate TOTP QR codes on the server side
woodruffw Apr 17, 2019
3066aff
tests: Add tests for TOTP QR generation
woodruffw Apr 17, 2019
8de81ce
warehouse: Use TOTP_LENGTH in form message
woodruffw Apr 18, 2019
8f48895
tests, warehouse: Remove vestigial token services
woodruffw Apr 18, 2019
0572986
tests: Parameterize OTP tests
woodruffw Apr 18, 2019
415ab89
tests: Fix import order
woodruffw Apr 18, 2019
421b19b
warehouse: Sign the query string during two factor login
woodruffw Apr 22, 2019
a954f94
tests: Update tests for signed query string
woodruffw Apr 22, 2019
3ff8cd4
warehouse: Remove TwoFactor model, add totp_secret to User
woodruffw Apr 22, 2019
db3e85b
tests: Update to reflect TwoFactor/User model changes
woodruffw Apr 22, 2019
7afef75
tests, warehouse: Remove unused exception
woodruffw Apr 22, 2019
be7474b
warehouse: Add TOTP validation metrics
woodruffw Apr 23, 2019
e31d291
tests: Update tests to cover new branches
woodruffw Apr 23, 2019
66bfe99
tests, warehouse: Feature-gate two factor usage
woodruffw Apr 26, 2019
e2348c1
warehouse/admin: Add 2FA control
woodruffw Apr 26, 2019
450c5d6
warehouse: Fix docstring, remove migration comments
woodruffw Apr 26, 2019
b127c8d
warehouse: Re-add spans for primary/verified emails
woodruffw Apr 26, 2019
54a32ee
warehouse: Add session warning when 2FA is disabled
woodruffw Apr 26, 2019
472a5a4
warehouse: Formatting nit on template
woodruffw Apr 26, 2019
3b369e6
tests, warehouse: Remove _login_user churn
woodruffw Apr 26, 2019
8bb88ed
warehouse/templates: Deduplicate TOTP application guidance
woodruffw Apr 26, 2019
68a3dbe
warehouse: Auto-format
woodruffw Apr 26, 2019
49cf749
warehouse: Link to TOTP help during provisioning
woodruffw Apr 29, 2019
2581ebe
warehouse: Revert cookie churn
woodruffw Apr 29, 2019
f0a2644
warehouse: Explicit encode() arguments
woodruffw Apr 29, 2019
80d3f5f
Include session notifications via CSI
di Apr 29, 2019
86c25ba
Merge branch 'master' into tob/996-add-2fa-support
woodruffw May 3, 2019
55869bf
tests: Remove unused import
woodruffw May 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ omit =

[report]
exclude_lines =
# pragma: no cover
pragma: no cover
class \w+\(Interface\):
1 change: 1 addition & 0 deletions dev/environment
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ STATUSPAGE_URL=https://2p66nmmycsj3.statuspage.io

TOKEN_PASSWORD_SECRET="an insecure password reset secret key"
TOKEN_EMAIL_SECRET="an insecure email verification secret key"
TOKEN_TWO_FACTOR_SECRET="an insecure two-factor auth secret key"

WAREHOUSE_LEGACY_DOMAIN=pypi.python.org
1 change: 1 addition & 0 deletions requirements/main.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ passlib>=1.6.4
premailer
psycopg2
pycurl
pyqrcode
pyramid>=1.9,<1.11.0
pyramid_jinja2>=2.5
pyramid_mailer>=0.14.1
Expand Down
3 changes: 3 additions & 0 deletions requirements/main.txt
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,9 @@ pycurl==7.43.0.2 \
--hash=sha256:df6f42787c39304522a00ecaa0a7fac5daffd0aa89044c68fc9fd5d683dd2ed5 \
--hash=sha256:eccea049aef47decc380746b3ff242d95636d578c907d0eab3b00918292d6c48 \
--hash=sha256:fe12f59e7bc6c217f12c6726c2617238fd4c0d53b28db956de592252da4e5bb0
pyqrcode==1.2.1 \
--hash=sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6 \
--hash=sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5
pygments==2.3.1 \
--hash=sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a \
--hash=sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d
Expand Down
1 change: 1 addition & 0 deletions tests/common/db/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class Meta:
datetime.datetime(2005, 1, 1), datetime.datetime(2010, 1, 1)
)
last_login = factory.fuzzy.FuzzyNaiveDateTime(datetime.datetime(2011, 1, 1))
two_factor_allowed = True


class EmailFactory(WarehouseFactory):
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/accounts/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ def test_includeme(monkeypatch):
TokenServiceFactory(name="password"), ITokenService, name="password"
),
pretend.call(TokenServiceFactory(name="email"), ITokenService, name="email"),
pretend.call(
TokenServiceFactory(name="two_factor"), ITokenService, name="two_factor"
),
pretend.call(
HaveIBeenPwnedPasswordBreachedService.create_service,
IPasswordBreachedService,
Expand Down
39 changes: 39 additions & 0 deletions tests/unit/accounts/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,3 +553,42 @@ def test_password_breached(self):
"This password has appeared in a breach or has otherwise been "
"compromised and cannot be used."
)


class TestTwoFactorForm:
def test_creation(self):
user_id = pretend.stub()
user_service = pretend.stub()
form = forms.TwoFactorForm(user_id=user_id, user_service=user_service)

assert form.user_service is user_service

def test_totp_secret_exists(self):
form = forms.TwoFactorForm(
data={"totp_value": ""}, user_id=pretend.stub(), user_service=pretend.stub()
)
assert not form.validate()
assert form.totp_value.errors.pop() == "This field is required."

form = forms.TwoFactorForm(
data={"totp_value": "not_a_real_value"},
user_id=pretend.stub(),
user_service=pretend.stub(check_totp_value=lambda *a: True),
)
assert not form.validate()
assert form.totp_value.errors.pop() == "TOTP code must be 6 digits."

form = forms.TwoFactorForm(
data={"totp_value": "123456"},
user_id=pretend.stub(),
user_service=pretend.stub(check_totp_value=lambda *a: False),
)
assert not form.validate()
assert form.totp_value.errors.pop() == "Invalid TOTP code."

form = forms.TwoFactorForm(
data={"totp_value": "123456"},
user_id=pretend.stub(),
user_service=pretend.stub(check_totp_value=lambda *a: True),
)
assert form.validate()
22 changes: 22 additions & 0 deletions tests/unit/accounts/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

from zope.interface.verify import verifyClass

import warehouse.utils.otp as otp

from warehouse.accounts import services
from warehouse.accounts.interfaces import (
IPasswordBreachedService,
Expand Down Expand Up @@ -342,6 +344,26 @@ def test_updating_password_undisables(self, user_service):
user_service.update_user(user.id, password="foo")
assert user_service.is_disabled(user.id) == (False, None)

def test_has_two_factor(self, user_service):
user = UserFactory.create()
assert not user_service.has_two_factor(user.id)
user_service.update_user(user.id, totp_secret=b"foobar")
assert user_service.has_two_factor(user.id)

@pytest.mark.parametrize("valid", [True, False])
def test_check_totp_value(self, user_service, monkeypatch, valid):
verify_totp = pretend.call_recorder(lambda *a: valid)
monkeypatch.setattr(otp, "verify_totp", verify_totp)

user = UserFactory.create()
user_service.update_user(user.id, totp_secret=b"foobar")

assert user_service.check_totp_value(user.id, b"123456") == valid

def test_check_totp_value_no_secret(self, user_service):
user = UserFactory.create()
assert not user_service.check_totp_value(user.id, b"123456")


class TestTokenService:
def test_verify_service(self):
Expand Down
Loading