-
Notifications
You must be signed in to change notification settings - Fork 52
1416 add support for mfa for tom user logins #1653
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
base: dev
Are you sure you want to change the base?
Changes from all commits
de82704
1da9a62
6ceaaee
e9e3d63
f55a32d
5bd3a9c
a1d51c0
423a442
66d0924
611deee
96eaeab
4484637
336f319
7553294
46b3745
62668e5
c8eafca
9b7689d
06c3978
c01321e
e662a2a
996464d
30fab14
b91373d
2a2c444
a7a2797
391f752
1cf8d84
d6fa248
4e6336a
62ee04e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,3 +24,11 @@ a.hs { | |
| color: #017792 !important; | ||
| font-weight: bold; | ||
| } | ||
|
|
||
| /* Sphinx's basic.css makes inline-literal tokens (span.pre) no-wrap; a long dotted path in a | ||
| table cell then widens its column past the table's declared :widths:. Let literals wrap | ||
| inside tables so column hints hold ("anywhere" shrinks min-content width, "break-word" won't). */ | ||
| table.docutils span.pre { | ||
| white-space: normal; | ||
| overflow-wrap: anywhere; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. breaking anywhere makes the long dotted path much harder to read and probably means you shouldn't be using tables this way... |
||
| } | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| Common Accounts Scenarios | ||
| ========================= | ||
|
|
||
| Three worked configurations combining the controls from :doc:`Accounts and Authentication | ||
| <authentication>`. Each shows the complete ``settings.py`` recipe and what your users | ||
| experience. Settings not shown keep their defaults; every setting is documented in | ||
| :doc:`Custom settings <customsettings>`. | ||
|
|
||
| A public TOM with optional two-factor authentication | ||
| ---------------------------------------------------- | ||
|
|
||
| For a TOM whose pages are public (the default ``READ_ONLY`` strategy) and whose | ||
| collaborators get accounts from an administrator. This is the out-of-the-box configuration — | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all of these em dashes look so bad. |
||
| there is nothing to add: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| # settings.py: nothing! This is the default behavior. | ||
|
|
||
| What you get: anonymous visitors browse but cannot change anything; administrators create | ||
| accounts from the *Users* page; every user *may* enable two-factor authentication from the | ||
| *Security* card on their profile page, and the *Users* page shows who has. To require MFA on | ||
| privileged accounts while leaving it optional otherwise:: | ||
|
|
||
| TOM_MFA_REQUIRED = 'superusers' | ||
|
|
||
| A locked collaboration TOM with self-registration | ||
| ------------------------------------------------- | ||
|
|
||
| For a TOM serving a collaboration whose data is private, but whose membership is broad enough | ||
| that administrators should not create every account by hand — new members register themselves | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. they just let everyone know that you didn't write this. |
||
| and an administrator approves each one. | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| AUTH_STRATEGY = 'LOCKED' | ||
| TOM_REGISTRATION_STRATEGY = 'approval_required' | ||
| TOM_PASSWORD_RESET_ENABLED = True | ||
| TOM_REQUIRED_USER_FIELDS = ['first_name', 'last_name', 'email', 'affiliation'] | ||
|
|
||
| # the notifications this flow sends need working email: | ||
| EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' | ||
| EMAIL_HOST = os.environ['EMAIL_HOST'] # and EMAIL_PORT, EMAIL_USE_TLS, credentials | ||
| DEFAULT_FROM_EMAIL = SERVER_EMAIL = os.environ['DEFAULT_FROM_EMAIL'] | ||
| MANAGERS = [('TOM administrators', os.environ['TOM_ADMIN_EMAIL'])] | ||
|
|
||
| What you get: every TOM page requires login (exception: pages required for registration; | ||
| the login, sign-up, pending-approval, and password-reset pages are open automatically — | ||
| no ``OPEN_URLS`` needed). You also get a *Register* button in the navbar; applicants fill | ||
| in the required fields, wait on the "pending approval" page, and ``MANAGERS`` are emailed; | ||
| an administrator approves from the *Pending users* table and the new member is emailed a | ||
| login link. Without working email the flow still works, but you | ||
| must tell applicants yourself — the pages say so, and ``manage.py check`` warns of this | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does claud not know what a semi-colon is? Why did Emily Dickenson have so much influence on the AI? |
||
| (no email) situation. | ||
|
|
||
| A strict hosting policy | ||
| ----------------------- | ||
|
|
||
| For a TOM whose hosting environment mandates more elaborate security: mandatory two-factor | ||
| authentication, password composition and expiry rules, terms-of-service acceptance, and | ||
| API-token controls. | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| TOM_MFA_REQUIRED = 'all' | ||
| TOM_PASSWORD_EXPIRY_DAYS = 60 | ||
| TOM_TERMS_OF_SERVICE_VERSION = '2026-09-01' # bump the string to require re-acceptance | ||
| TOM_REQUIRED_USER_FIELDS = ['first_name', 'last_name', 'email', 'affiliation', 'phone_number'] | ||
| TOM_API_TOKEN_EXPIRY_DAYS = 60 | ||
| TOM_API_TOKEN_REQUIRES_MFA = True | ||
|
|
||
| AUTH_PASSWORD_VALIDATORS = [ | ||
| {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, | ||
| {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 12}}, | ||
| {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, | ||
| {'NAME': 'tom_common.accounts.password_validation.CharacterClassValidator'}, | ||
| {'NAME': 'tom_common.accounts.password_validation.NotSameAsCurrentPasswordValidator'}, | ||
| ] | ||
|
|
||
| REST_FRAMEWORK = { | ||
| 'DEFAULT_AUTHENTICATION_CLASSES': [ | ||
| 'tom_common.accounts.api_auth.TomTokenAuthentication', # API token requires MFA enabled | ||
| 'rest_framework.authentication.SessionAuthentication', | ||
| # no BasicAuthentication: per-request passwords would bypass the second factor | ||
| ], | ||
| 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'], | ||
| } | ||
|
|
||
| Write your terms of service in ``templates/tom_common/partials/terms_of_service_text.html``. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where should they keep this template? |
||
| What you get: after logging in, every user is walked through — in order — accepting the terms, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seriously, nobody in the real world writes this way. |
||
| enrolling an authenticator app, replacing an expired (or administrator-set) password, and | ||
| completing missing profile fields, each with a message saying why; then they continue to | ||
| wherever they were going. API tokens expire, are honoured only for currently-enrolled users, | ||
| and can only be (re)generated by their owner after re-authenticating; the password-only | ||
| ``/api/token-auth/`` endpoint refuses. The *Users* page shows administrators one compliance | ||
| column per control. | ||
|
|
||
| Deployment reminders for this profile: a shared cache (rate limits and code replay | ||
| protection), ``ALLAUTH_TRUSTED_PROXY_COUNT`` behind a proxy, session-cookie hardening, and a | ||
| log destination for ``tom_common.security`` — see :ref:`auth-deployment-notes`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's basically like waving a flag and saying no human looked at this before it got published. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,8 @@ Configuring a TOM | |
|
|
||
| Custom settings <customsettings> | ||
| Permissions <permissions> | ||
| Accounts and authentication <authentication> | ||
| Common accounts scenarios <authentication_scenarios> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like it should pretty obviously be a sub-page under Accounts and Authentication |
||
|
|
||
| TOM Settings | ||
| ------------ | ||
|
|
@@ -33,3 +35,10 @@ Permissions | |
| TOM systems can have hundreds or thousands of users and we recognize that sometimes it is desirable to control | ||
| who can access what data or functions. The Toolkit provides fine-grained control over user permissions, as documented | ||
| :doc:`here</common/permissions>`. | ||
|
|
||
| Accounts and authentication | ||
| --------------------------- | ||
|
|
||
| TOM Toolkit uses `django-allauth <https://docs.allauth.org/en/latest/>`_ to provide login, optional | ||
| two-factor authentication, self-registration, password rules, terms of service, profile fields and API | ||
| tokens as described :doc:`here</common/authentication>`. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-write this to be normal conversational english that describes what it does.
"Make sure that long strings will wrap inside of table cells rather than extending column widths."