0001-fix-concurrency-error-when-creating-new-users-fixes-.patch
mellon/adapters.py | ||
---|---|---|
1 | 1 |
import logging |
2 |
import uuid |
|
2 | 3 | |
3 | 4 |
from django.core.exceptions import PermissionDenied |
4 | 5 |
from django.contrib import auth |
... | ... | |
53 | 54 |
issuer = saml_attributes['issuer'] |
54 | 55 |
try: |
55 | 56 |
return User.objects.get(saml_identifiers__name_id=name_id, |
56 |
saml_identifiers__issuer=issuer) |
|
57 |
saml_identifiers__issuer=issuer)
|
|
57 | 58 |
except User.DoesNotExist: |
58 | 59 |
if not utils.get_setting(idp, 'PROVISION'): |
60 |
self.logger.warning('provisionning disabled, login refused') |
|
59 | 61 |
return None |
60 | 62 |
username = self.format_username(idp, saml_attributes) |
61 | 63 |
if not username: |
64 |
self.logger.warning('could not build a username, login refused') |
|
62 | 65 |
return None |
63 |
user = User(username=username) |
|
64 |
user.save() |
|
65 |
self.provision_name_id(user, idp, saml_attributes) |
|
66 |
user = User.objects.create(username=uuid.uuid4().hex[:30]) |
|
67 |
saml_id, created = models.UserSAMLIdentifier.objects.get_or_create( |
|
68 |
name_id=name_id, issuer=issuer, defaults={'user': user}) |
|
69 |
if created: |
|
70 |
user.username = username |
|
71 |
user.save() |
|
72 |
else: |
|
73 |
user.delete() |
|
74 |
user = saml_id.user |
|
66 | 75 |
return user |
67 | 76 | |
68 | 77 |
def provision(self, user, idp, saml_attributes): |
... | ... | |
70 | 79 |
self.provision_superuser(user, idp, saml_attributes) |
71 | 80 |
self.provision_groups(user, idp, saml_attributes) |
72 | 81 | |
73 |
def provision_name_id(self, user, idp, saml_attributes): |
|
74 |
models.UserSAMLIdentifier.objects.get_or_create( |
|
75 |
user=user, |
|
76 |
issuer=saml_attributes['issuer'], |
|
77 |
name_id=saml_attributes['name_id_content']) |
|
78 | ||
79 | 82 |
def provision_attribute(self, user, idp, saml_attributes): |
80 | 83 |
realm = utils.get_setting(idp, 'REALM') |
81 | 84 |
attribute_mapping = utils.get_setting(idp, 'ATTRIBUTE_MAPPING') |
tests/conftest.py | ||
---|---|---|
1 |
import pytest |
|
2 | ||
3 | ||
4 |
@pytest.fixture |
|
5 |
def concurrency(settings): |
|
6 |
'''Select a level of concurrency based on the db, sqlite3 is less robust |
|
7 |
thant postgres due to its transaction lock timeout of 5 seconds. |
|
8 |
''' |
|
9 |
if 'sqlite' in settings.DATABASES['default']['ENGINE']: |
|
10 |
return 20 |
|
11 |
else: |
|
12 |
return 100 |
tests/test_default_adapter.py | ||
---|---|---|
1 |
import threading |
|
1 | 2 |
import pytest |
2 | 3 | |
3 | 4 |
from django.conf import settings |
... | ... | |
45 | 46 |
assert user is None |
46 | 47 |
assert User.objects.count() == 0 |
47 | 48 | |
49 | ||
50 |
def test_lookup_user_transaction(transactional_db, concurrency): |
|
51 |
adapter = DefaultAdapter() |
|
52 |
N = 30 |
|
53 |
def map_threads(f, l): |
|
54 |
threads = [] |
|
55 |
for i in l: |
|
56 |
threads.append(threading.Thread(target=f, args=(i,))) |
|
57 |
threads[-1].start() |
|
58 |
for thread in threads: |
|
59 |
thread.join() |
|
60 |
users = [] |
|
61 | ||
62 |
def f(i): |
|
63 |
users.append(adapter.lookup_user(idp, saml_attributes)) |
|
64 |
map_threads(f, range(concurrency)) |
|
65 |
assert len(users) == concurrency |
|
66 |
assert len(set(user.pk for user in users)) == 1 |
|
67 | ||
68 | ||
48 | 69 |
def test_provision(settings): |
49 | 70 |
settings.MELLON_GROUP_ATTRIBUTE = 'group' |
50 | 71 |
User = auth.get_user_model() |
testsettings.py | ||
---|---|---|
4 | 4 |
'default': { |
5 | 5 |
'ENGINE': 'django.db.backends.sqlite3', |
6 | 6 |
'NAME': 'mellon.sqlite3', |
7 |
'TEST': { |
|
8 |
'NAME': 'mellon-test.sqlite', |
|
9 |
}, |
|
7 | 10 |
} |
8 | 11 |
} |
9 | 12 |
DEBUG = True |
10 |
- |