Initial mfa support (for internal users) #93591
@ -1,7 +1,7 @@
|
|||||||
"""Give everybody with a NULL nickname something based on their full name."""
|
"""Give everybody with a NULL nickname something based on their full name."""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, IntegrityError, transaction
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def set_nickname(model, user, nickname) -> bool:
|
def set_nickname(model, user, nickname) -> bool:
|
||||||
@ -9,7 +9,6 @@ def set_nickname(model, user, nickname) -> bool:
|
|||||||
|
|
||||||
count = model.objects.filter(nickname=nickname).count()
|
count = model.objects.filter(nickname=nickname).count()
|
||||||
if count > 0:
|
if count > 0:
|
||||||
print(f'already {count} user(s) with nickname {nickname!r}')
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
user.nickname = nickname
|
user.nickname = nickname
|
||||||
@ -30,7 +29,6 @@ def random_nums():
|
|||||||
def fill_nicknames(apps, schema_editor):
|
def fill_nicknames(apps, schema_editor):
|
||||||
"""Give users with a NULL nickname something based on their full name."""
|
"""Give users with a NULL nickname something based on their full name."""
|
||||||
import re
|
import re
|
||||||
import datetime
|
|
||||||
|
|
||||||
# We can't import the User model directly as it may be a newer
|
# We can't import the User model directly as it may be a newer
|
||||||
# version than this migration expects. We use the historical version.
|
# version than this migration expects. We use the historical version.
|
||||||
@ -42,14 +40,10 @@ def fill_nicknames(apps, schema_editor):
|
|||||||
users = User.objects.filter(nickname=None)
|
users = User.objects.filter(nickname=None)
|
||||||
total_users = users.count()
|
total_users = users.count()
|
||||||
last_report = 0
|
last_report = 0
|
||||||
start = datetime.datetime.now()
|
|
||||||
print()
|
|
||||||
print(f' - migrating {total_users} users.')
|
|
||||||
for idx, user in enumerate(users):
|
for idx, user in enumerate(users):
|
||||||
perc = idx / total_users
|
perc = idx / total_users
|
||||||
if perc - last_report > 0.10:
|
if perc - last_report > 0.10:
|
||||||
last_report = perc
|
last_report = perc
|
||||||
print(f' - {idx} ({int(perc*100)}%)')
|
|
||||||
|
|
||||||
# We cannot migrate the entire database in one transaction (too many
|
# We cannot migrate the entire database in one transaction (too many
|
||||||
# queries for MySQL to handle), so do one transaction per user.
|
# queries for MySQL to handle), so do one transaction per user.
|
||||||
@ -63,9 +57,6 @@ def fill_nicknames(apps, schema_editor):
|
|||||||
if set_nickname(User, user, f'{base}-{num}'):
|
if set_nickname(User, user, f'{base}-{num}'):
|
||||||
break
|
break
|
||||||
|
|
||||||
end = datetime.datetime.now() # assume the timezone hasn't changed.
|
|
||||||
print(f'Migration of {total_users} took {end - start}')
|
|
||||||
|
|
||||||
|
|
||||||
def fake_reverse(apps, schema_editor):
|
def fake_reverse(apps, schema_editor):
|
||||||
"""Allow reversal of this migration really reversing."""
|
"""Allow reversal of this migration really reversing."""
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
{% block confirm_email_body %}
|
{% block confirm_email_body %}
|
||||||
<h2>Confirm your email address</h2>
|
<h2>Confirm your email address</h2>
|
||||||
<p>
|
<p>
|
||||||
We have sent an email with instructions to <strong>{{ user.email_to_confirm }}</strong>.
|
We have sent an email with instructions to <strong>{{ user.email_to_confirm }}</strong>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If the email doesn't arrive shortly, please check your spam folder.
|
If the email doesn't arrive shortly, please check your spam folder.
|
||||||
</p>
|
</p>
|
||||||
<div id="poll_ok" style="display: non">
|
<div id="poll_ok" style="display: none">
|
||||||
<div class="alert alert-success">
|
<div class="alert alert-success">
|
||||||
Your email address has been confirmed.
|
Your email address has been confirmed.
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-row justify-content-center mt-3">
|
<div class="btn-row justify-content-center mt-3">
|
||||||
<a class="btn btn-primary px-5" href="{% url 'bid_main:index' %}">Done</a>
|
<a class="btn btn-primary px-5" href="{% url 'bid_main:index' %}">Done</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -24,26 +24,26 @@
|
|||||||
var poll_url = '{% url 'bid_main:confirm-email-poll' %}';
|
var poll_url = '{% url 'bid_main:confirm-email-poll' %}';
|
||||||
var $result = $('#poll_result');
|
var $result = $('#poll_result');
|
||||||
|
|
||||||
function poll() {
|
function poll(attemptCount) {
|
||||||
$.get(poll_url)
|
if (attemptCount > 5) {
|
||||||
.done(function(data) {
|
// Stop, don't poll forever
|
||||||
if (typeof(data.confirmed) === 'undefined' || data.confirmed == null) {
|
return;
|
||||||
// Not yet confirmed, wait longer
|
}
|
||||||
window.setTimeout(poll, 2500);
|
$.get(poll_url)
|
||||||
return;
|
.done(function(data) {
|
||||||
}
|
if (typeof(data.confirmed) === 'undefined' || data.confirmed == null) {
|
||||||
// Confirmation has been confirmed!
|
// Not yet confirmed, wait longer with exponential backoff
|
||||||
$('#poll_ok').show();
|
attemptCount += 1;
|
||||||
})
|
window.setTimeout(poll, 10000 * (2 ** attemptCount), attemptCount);
|
||||||
.fail(function(err) {
|
return;
|
||||||
// This is a frequent poll and the email address confirmation system works
|
}
|
||||||
// fine without it, so let's not bother the user with error messages.
|
// Confirmation has been confirmed!
|
||||||
if (console) console.log('Error: ', err);
|
$('#poll_ok').show();
|
||||||
|
})
|
||||||
// Try again after a while.
|
.fail(function(err) {
|
||||||
window.setTimeout(poll, 4000);
|
// Stop on error
|
||||||
});
|
if (console) console.log('Error: ', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Do a quick test first, then slow down a little bit.
|
window.setTimeout(poll, 1000, 0);
|
||||||
window.setTimeout(poll, 250);
|
|
||||||
</script>{% endblock footer_scripts %}
|
</script>{% endblock footer_scripts %}
|
||||||
|
@ -146,11 +146,9 @@ TEMPLATES = [
|
|||||||
WSGI_APPLICATION = "blenderid.wsgi.application"
|
WSGI_APPLICATION = "blenderid.wsgi.application"
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': dj_database_url.config(
|
'default': dj_database_url.config(default='sqlite:///{}'.format(BASE_DIR / 'db.sqlite3')),
|
||||||
default='postgresql://blender_id:blender_id@127.0.0.1:5432/blender_id',
|
|
||||||
conn_max_age=600,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
DATABASES['default']['CONN_MAX_AGE'] = 600
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ To encrypt this value, use the following command:
|
|||||||
|
|
||||||
echo -n 'https://foo@bar.example.com/1234' | ansible-vault encrypt_string --vault-id production@prompt --stdin-name 'sentry_dsn'
|
echo -n 'https://foo@bar.example.com/1234' | ansible-vault encrypt_string --vault-id production@prompt --stdin-name 'sentry_dsn'
|
||||||
|
|
||||||
Store the ouput of the above command in `environments/production/group_vars/all/99_vault.yaml`
|
Store the output of the above command in `environments/production/group_vars/all/99_vault.yaml`
|
||||||
(not tracked by this repository):
|
(not tracked by this repository):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
env: staging
|
env: staging
|
||||||
host: web-2.internal
|
host: web-staging-1.hz-nbg1.blender.internal
|
||||||
db_host: db-postgres-2.internal
|
db_host: db-postgres-staging-1.hz-nbg1.blender.internal
|
||||||
db_name: id_staging
|
db_name: id_staging
|
||||||
db_user: id_staging
|
db_user: id_staging
|
||||||
branch: main
|
branch: main
|
||||||
|
|
||||||
ssl_only: true
|
ssl_only: true
|
||||||
|
|
||||||
|
backup_dir: /data/backups
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
ingress:
|
ingress:
|
||||||
hosts:
|
hosts:
|
||||||
lb-2:
|
lb-staging-1.hz-nbg1.blender.internal:
|
||||||
|
|
||||||
application:
|
application:
|
||||||
hosts:
|
hosts:
|
||||||
web-2:
|
web-staging-1.hz-nbg1.blender.internal:
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit f3e705b712980c5d2bce58dd94b51665858a2dc3
|
Subproject commit 2e9a85dc8b2f9bd3cb807de5a489615355b50f70
|
@ -36,15 +36,16 @@ rate_limit:
|
|||||||
burst: 50
|
burst: 50
|
||||||
delay: 10
|
delay: 10
|
||||||
|
|
||||||
mailto: cron@blender.org
|
|
||||||
certbot:
|
certbot:
|
||||||
email: root@blender.org
|
email: root@blender.org
|
||||||
|
|
||||||
source_url: https://projects.blender.org/infrastructure/{{ project_slug }}.git
|
source_url: https://projects.blender.org/infrastructure/{{ project_slug }}.git
|
||||||
branch: production
|
branch: production
|
||||||
|
|
||||||
|
backup_dir: /mnt/backup
|
||||||
|
|
||||||
ssl_only: false
|
ssl_only: false
|
||||||
PGSSLROOTCERT: /usr/local/share/ca-certificates/cloud-init-ca-cert-1.crt
|
ca_certificate: /usr/local/share/ca-certificates/cloud-init-ca-cert-1.crt
|
||||||
|
|
||||||
include_common_services:
|
include_common_services:
|
||||||
- background
|
- background
|
||||||
@ -52,4 +53,5 @@ include_common_services:
|
|||||||
- backup-service-dirs
|
- backup-service-dirs
|
||||||
- clearsessions
|
- clearsessions
|
||||||
- delete-completed-tasks
|
- delete-completed-tasks
|
||||||
|
- notify-email@
|
||||||
- process-deletion-requests
|
- process-deletion-requests
|
||||||
|
@ -37,7 +37,6 @@ more-itertools==7.2.0 ; python_version >= "3.8" and python_version < "4"
|
|||||||
oauthlib==3.1.0 ; python_version >= "3.8" and python_version < "4"
|
oauthlib==3.1.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
pep562==1.0 ; python_version >= "3.8" and python_version < "4"
|
pep562==1.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
pillow==9.1.0 ; python_version >= "3.8" and python_version < "4"
|
pillow==9.1.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
psycopg2==2.8.6 ; python_version >= "3.8" and python_version < "4"
|
|
||||||
pycparser==2.19 ; python_version >= "3.8" and python_version < "4"
|
pycparser==2.19 ; python_version >= "3.8" and python_version < "4"
|
||||||
pygments==2.17.2 ; python_version >= "3.8" and python_version < "4"
|
pygments==2.17.2 ; python_version >= "3.8" and python_version < "4"
|
||||||
pyinstrument==4.6.0 ; python_version >= "3.8" and python_version < "4"
|
pyinstrument==4.6.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
@ -58,6 +57,5 @@ sqlparse==0.5.0 ; python_version >= "3.8" and python_version < "4"
|
|||||||
tornado==6.0.3 ; python_version >= "3.8" and python_version < "4"
|
tornado==6.0.3 ; python_version >= "3.8" and python_version < "4"
|
||||||
urllib3==1.25.11 ; python_version >= "3.8" and python_version < "4"
|
urllib3==1.25.11 ; python_version >= "3.8" and python_version < "4"
|
||||||
user-agents==2.2.0
|
user-agents==2.2.0
|
||||||
uwsgi==2.0.23
|
|
||||||
wrapt==1.15.0 ; python_version >= "3.8" and python_version < "4"
|
wrapt==1.15.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
zipp==0.6.0 ; python_version >= "3.8" and python_version < "4"
|
zipp==0.6.0 ; python_version >= "3.8" and python_version < "4"
|
||||||
|
@ -1 +1,3 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
|
psycopg2==2.8.6
|
||||||
|
uwsgi==2.0.23
|
||||||
|
Loading…
Reference in New Issue
Block a user