0001-misc-monkeypatch-user-model-with-a-method-to-get-nam.patch
combo/apps/lingo/views.py | ||
---|---|---|
40 | 40 | |
41 | 41 |
from combo.data.models import Page |
42 | 42 |
from combo.utils import check_request_signature, aes_hex_decrypt, DecryptionError |
43 | ||
44 |
if 'mellon' in settings.INSTALLED_APPS: |
|
45 |
from mellon.models import UserSAMLIdentifier |
|
46 |
else: |
|
47 |
UserSAMLIdentifier = None |
|
43 |
from combo.profile.utils import get_user_from_name_id |
|
48 | 44 | |
49 | 45 |
from .models import (Regie, BasketItem, Transaction, TransactionOperation, |
50 | 46 |
LingoBasketCell, SelfDeclaredInvoicePayment) |
... | ... | |
125 | 121 | |
126 | 122 |
try: |
127 | 123 |
if request.GET.get('NameId'): |
128 |
if UserSAMLIdentifier is None: |
|
129 |
raise Exception('missing mellon?') |
|
130 |
try: |
|
131 |
user = UserSAMLIdentifier.objects.get(name_id=request.GET.get('NameId')).user |
|
132 |
except UserSAMLIdentifier.DoesNotExist: |
|
133 |
raise Exception('unknown name id') |
|
124 |
user = get_user_from_name_id(request.GET.get('NameId')) |
|
125 |
if user is None: |
|
126 |
raise User.DoesNotExist() |
|
134 | 127 |
elif request.GET.get('email'): |
135 | 128 |
user = User.objects.get(email=request.GET.get('email')) |
136 | 129 |
else: |
... | ... | |
198 | 191 | |
199 | 192 |
try: |
200 | 193 |
if request.GET.get('NameId'): |
201 |
if UserSAMLIdentifier is None: |
|
202 |
raise Exception('missing mellon?') |
|
203 |
try: |
|
204 |
user = UserSAMLIdentifier.objects.get(name_id=request.GET.get('NameId')).user |
|
205 |
except UserSAMLIdentifier.DoesNotExist: |
|
206 |
raise Exception('unknown name id') |
|
194 |
user = get_user_from_name_id(request.GET.get('NameId')) |
|
195 |
if user is None: |
|
196 |
raise User.DoesNotExist() |
|
207 | 197 |
elif request.GET.get('email'): |
208 | 198 |
user = User.objects.get(email=request.GET.get('email')) |
209 | 199 |
else: |
combo/apps/newsletters/forms.py | ||
---|---|---|
38 | 38 |
logger.error('Error occured while getting newsletters: %r', e) |
39 | 39 |
return |
40 | 40 |
self.params = {} |
41 |
if hasattr(self.user, 'saml_identifiers') and self.user.saml_identifiers.exists(): |
|
42 |
self.params['uuid'] = self.user.saml_identifiers.first().name_id |
|
41 |
user_name_id = self.user.get_name_id() |
|
42 |
if user_name_id: |
|
43 |
self.params['uuid'] = user_name_id |
|
43 | 44 | |
44 | 45 |
# get mobile number from mellon session as it is not user attribute |
45 | 46 |
if self.request.session.get('mellon_session'): |
combo/apps/wcs/models.py | ||
---|---|---|
313 | 313 | |
314 | 314 |
def get_api_url(self, context): |
315 | 315 |
user = self.get_concerned_user(context) |
316 |
if hasattr(user, 'saml_identifiers') and user.saml_identifiers.exists(): |
|
317 |
return '/api/users/%s/forms' % user.saml_identifiers.first().name_id |
|
316 |
if user: |
|
317 |
user_name_id = user.get_name_id() |
|
318 |
if user_name_id: |
|
319 |
return '/api/users/%s/forms' % user_name_id |
|
318 | 320 |
return '/api/user/forms' |
319 | 321 | |
320 | 322 |
@property |
... | ... | |
386 | 388 | |
387 | 389 |
def get_api_url(self, context): |
388 | 390 |
user = self.get_concerned_user(context) |
389 |
if hasattr(user, 'saml_identifiers') and user.saml_identifiers.exists(): |
|
390 |
return '/api/users/%s/drafts' % user.saml_identifiers.first().name_id |
|
391 |
if user: |
|
392 |
user_name_id = user.get_name_id() |
|
393 |
if user_name_id: |
|
394 |
return '/api/users/%s/drafts' % user_name_id |
|
391 | 395 |
return '/api/user/drafts' |
392 | 396 | |
393 | 397 |
def get_cell_extra_context(self, context): |
combo/profile/__init__.py | ||
---|---|---|
21 | 21 |
from django.utils.translation import ugettext_lazy as _ |
22 | 22 | |
23 | 23 | |
24 |
def user_get_name_id(user): |
|
25 |
saml_identifier = user.saml_identifiers.first() |
|
26 |
if saml_identifier: |
|
27 |
return saml_identifier.name_id |
|
28 |
return None |
|
29 | ||
30 | ||
24 | 31 |
class AppConfig(django.apps.AppConfig): |
25 | 32 |
name = 'combo.profile' |
26 | 33 |
verbose_name = _('Profile') |
... | ... | |
28 | 35 |
def ready(self): |
29 | 36 |
from combo.apps.search import engines |
30 | 37 |
engines.register(self.get_search_engines) |
38 |
from django.contrib.auth import get_user_model |
|
39 |
get_user_model().add_to_class('get_name_id', user_get_name_id) |
|
31 | 40 | |
32 | 41 |
def get_search_engines(self): |
33 | 42 |
from combo.data.models import Page |
combo/profile/utils.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2014-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf import settings |
|
18 | ||
19 | ||
20 |
if 'mellon' in settings.INSTALLED_APPS: |
|
21 |
from mellon.models import UserSAMLIdentifier |
|
22 |
else: |
|
23 |
UserSAMLIdentifier = None |
|
24 | ||
25 | ||
26 |
def get_user_from_name_id(name_id): |
|
27 |
if not UserSAMLIdentifier: |
|
28 |
return None |
|
29 |
try: |
|
30 |
return UserSAMLIdentifier.objects.get(name_id=name_id).user |
|
31 |
except UserSAMLIdentifier.DoesNotExist: |
|
32 |
return None |
combo/public/templatetags/combo.py | ||
---|---|---|
34 | 34 |
from combo.utils import NothingInCacheException, flatten_context |
35 | 35 |
from combo.apps.dashboard.models import DashboardCell, Tile |
36 | 36 | |
37 |
if 'mellon' in settings.INSTALLED_APPS: |
|
38 |
from mellon.models import UserSAMLIdentifier |
|
39 | ||
40 | 37 |
register = template.Library() |
41 | 38 | |
42 | 39 |
def skeleton_text(context, placeholder_name, content=''): |
... | ... | |
248 | 245 | |
249 | 246 |
@register.filter |
250 | 247 |
def name_id(user): |
251 |
saml_id = UserSAMLIdentifier.objects.filter(user=user).last() |
|
252 |
if saml_id: |
|
253 |
return saml_id.name_id |
|
248 |
if user: |
|
249 |
user_name_id = user.get_name_id() |
|
250 |
if user_name_id: |
|
251 |
return user_name_id |
|
254 | 252 |
# it is important to raise this so get_templated_url is aborted and no call |
255 | 253 |
# is tried with a missing user argument. |
256 | 254 |
raise VariableDoesNotExist('name_id') |
combo/public/views.py | ||
---|---|---|
45 | 45 | |
46 | 46 |
if 'mellon' in settings.INSTALLED_APPS: |
47 | 47 |
from mellon.utils import get_idps |
48 |
from mellon.models import UserSAMLIdentifier |
|
49 | 48 |
else: |
50 | 49 |
get_idps = lambda: [] |
51 |
UserSAMLIdentifier = None |
|
52 | 50 | |
53 | 51 |
from combo.data.models import (CellBase, PostException, Page, Redirect, |
54 | 52 |
ParentContentCell, TextCell, PageSnapshot) |
55 | 53 |
from combo.profile.models import Profile |
54 |
from combo.profile.utils import get_user_from_name_id |
|
56 | 55 |
from combo.apps.search.models import SearchCell |
57 | 56 |
from combo import utils |
58 | 57 | |
... | ... | |
81 | 80 |
ctx['selected_user'] = User.objects.get(id=ctx['user_id']) |
82 | 81 |
except (User.DoesNotExist, ValueError): |
83 | 82 |
pass |
84 |
if 'name_id' in ctx and UserSAMLIdentifier: |
|
85 |
try: |
|
86 |
ctx['selected_user'] = UserSAMLIdentifier.objects.get(name_id=ctx['name_id']).user |
|
87 |
except UserSAMLIdentifier.DoesNotExist: |
|
88 |
pass |
|
83 |
if 'name_id' in ctx: |
|
84 |
ctx['selected_user'] = get_user_from_name_id(ctx['name_id']) |
|
89 | 85 | |
90 | 86 |
@csrf_exempt |
91 | 87 |
def ajax_page_cell(request, page_pk, cell_reference): |
combo/utils/requests_wrapper.py | ||
---|---|---|
79 | 79 |
else: |
80 | 80 |
query_params = {} |
81 | 81 |
if federation_key == 'nameid': |
82 |
query_params['NameID'] = user.saml_identifiers.first().name_id
|
|
82 |
query_params['NameID'] = user.get_name_id()
|
|
83 | 83 |
elif federation_key == 'email': |
84 | 84 |
query_params['email'] = user.email |
85 | 85 |
else: # 'auto' |
86 |
if hasattr(user, 'saml_identifiers') and user.saml_identifiers.exists(): |
|
87 |
query_params['NameID'] = user.saml_identifiers.first().name_id |
|
86 |
user_name_id = user.get_name_id() |
|
87 |
if user_name_id: |
|
88 |
query_params['NameID'] = user_name_id |
|
88 | 89 |
else: |
89 | 90 |
query_params['email'] = user.email |
90 | 91 |
combo/utils/urls.py | ||
---|---|---|
42 | 42 |
user = getattr(context.get('request'), 'user', None) |
43 | 43 |
if user and user.is_authenticated(): |
44 | 44 |
template_vars['user_email'] = quote(user.email) |
45 |
if hasattr(user, 'saml_identifiers') and user.saml_identifiers.exists(): |
|
46 |
template_vars['user_nameid'] = quote(user.saml_identifiers.first().name_id) |
|
45 |
user_nameid = user.get_name_id() |
|
46 |
if user_nameid: |
|
47 |
template_vars['user_nameid'] = quote(user_nameid) |
|
47 | 48 |
template_vars.update(settings.TEMPLATE_VARS) |
48 | 49 |
if '{{' in url or '{%' in url: # Django template |
49 | 50 |
try: |
tests/test_lingo_remote_regie.py | ||
---|---|---|
75 | 75 |
def is_authenticated(self): |
76 | 76 |
return True |
77 | 77 | |
78 |
def __init__(self): |
|
79 |
class MockSAMLUsers(object): |
|
80 |
def exists(self): |
|
81 |
return True |
|
82 |
def first(self): |
|
83 |
class MockSAMLUser(object): |
|
84 |
name_id = 'r2d2' |
|
85 |
return MockSAMLUser() |
|
86 |
self.saml_identifiers = MockSAMLUsers() |
|
78 |
def get_name_id(self): |
|
79 |
return 'r2d2' |
|
80 | ||
87 | 81 | |
88 | 82 |
@mock.patch('combo.utils.requests_wrapper.RequestsSession.request') |
89 | 83 |
def test_remote_regie_active_invoices_cell(mock_request, remote_regie): |
tests/test_newsletters_cell.py | ||
---|---|---|
223 | 223 | |
224 | 224 |
fake_saml_request = mock.Mock() |
225 | 225 |
fake_saml_request.user = mock.Mock(email=USER_EMAIL) |
226 |
fake_saml_request.user.saml_identifiers = mock.Mock() |
|
227 |
fake_saml_request.user.saml_identifiers.exists.return_value = True |
|
228 |
fake_saml_request.user.saml_identifiers.first.return_value = mock.Mock(name_id='nameid') |
|
226 |
fake_saml_request.user.get_name_id.return_value = 'nameid' |
|
229 | 227 |
fake_saml_request.session = {'mellon_session': {'mobile': '0607080900'}} |
230 | 228 | |
231 | 229 |
form = NewslettersManageForm(instance=cell, request=fake_saml_request) |
tests/test_profile.py | ||
---|---|---|
13 | 13 | |
14 | 14 |
@override_settings( |
15 | 15 |
KNOWN_SERVICES={'authentic': {'idp': {'title': 'IdP', 'url': 'http://example.org/'}}}) |
16 |
@mock.patch('combo.public.templatetags.combo.UserSAMLIdentifier') |
|
17 | 16 |
@mock.patch('combo.utils.requests.get') |
18 |
def test_profile_cell(requests_get, user_saml, app, admin_user):
|
|
17 |
def test_profile_cell(requests_get, app, admin_user): |
|
19 | 18 |
page = Page() |
20 | 19 |
page.save() |
21 | 20 | |
... | ... | |
28 | 27 |
json=lambda: data, |
29 | 28 |
status_code=200) |
30 | 29 | |
31 |
def filter_mock(user=None): |
|
32 |
assert user is admin_user |
|
33 |
return mock.Mock(last=lambda: mock.Mock(name_id='123456')) |
|
34 | ||
35 |
mocked_objects = mock.Mock() |
|
36 |
mocked_objects.filter = mock.Mock(side_effect=filter_mock) |
|
37 |
user_saml.objects = mocked_objects |
|
30 |
admin_user.get_name_id = lambda: '123456' |
|
38 | 31 | |
39 | 32 |
context = cell.get_cell_extra_context({'synchronous': True, 'selected_user': admin_user}) |
40 | 33 |
assert context['profile_fields']['first_name']['value'] == 'Foo' |
41 | 34 |
assert context['profile_fields']['birthdate']['value'] == datetime.date(2018, 8, 10) |
42 | 35 |
assert requests_get.call_args[0][0] == 'http://example.org/api/users/123456/' |
43 | ||
44 |
def filter_mock_missing(user=None): |
|
45 |
return mock.Mock(last=lambda: None) |
|
46 | ||
47 |
mocked_objects.filter = mock.Mock(side_effect=filter_mock_missing) |
|
48 | ||
49 |
context = cell.get_cell_extra_context({'synchronous': True, 'selected_user': admin_user}) |
|
50 |
assert context['error'] == 'unknown user' |
|
51 |
assert requests_get.call_count == 1 # no new call was made |
tests/test_public.py | ||
---|---|---|
814 | 814 |
assert 'XXYY' in resp.text |
815 | 815 | |
816 | 816 |
# custom behaviour for <name_id>, it will add the SAML user to context |
817 |
with mock.patch('combo.public.views.UserSAMLIdentifier') as user_saml:
|
|
817 |
with mock.patch('combo.profile.utils.UserSAMLIdentifier') as user_saml:
|
|
818 | 818 |
class DoesNotExist(Exception): |
819 | 819 |
pass |
820 | 820 |
user_saml.DoesNotExist = DoesNotExist |
tests/test_requests.py | ||
---|---|---|
8 | 8 | |
9 | 9 |
from combo.utils import requests, check_query, NothingInCacheException |
10 | 10 | |
11 |
class MockSAMLUser(object): |
|
12 |
name_id = 'r2d2' |
|
13 | ||
14 | 11 |
class MockUser(object): |
15 | 12 |
email = 'foo@example.net' |
16 | 13 |
def is_authenticated(self): |
17 | 14 |
return True |
18 | 15 | |
16 |
def get_name_id(self): |
|
17 |
if self.samlized: |
|
18 |
return 'r2d2' |
|
19 |
return None |
|
20 | ||
19 | 21 |
def __init__(self, samlized=True): |
20 |
class MockSAMLUsers(object): |
|
21 |
def exists(self): |
|
22 |
return True |
|
23 |
def first(self): |
|
24 |
return MockSAMLUser() |
|
25 |
if samlized: |
|
26 |
self.saml_identifiers = MockSAMLUsers() |
|
22 |
self.samlized = samlized |
|
27 | 23 | |
28 | 24 | |
29 | 25 |
def test_nosign(): |
tests/test_utils.py | ||
---|---|---|
9 | 9 |
from django.contrib.auth.models import AnonymousUser |
10 | 10 | |
11 | 11 | |
12 |
class MockSAMLUser(object): |
|
13 |
name_id = 'r2&d2' |
|
14 | ||
15 | 12 |
class MockUser(object): |
16 | 13 |
email = 'foo=3@example.net' |
17 | 14 |
def is_authenticated(self): |
18 | 15 |
return True |
19 | 16 | |
20 | 17 |
def __init__(self, samlized=True): |
21 |
class MockSAMLUsers(object): |
|
22 |
def exists(self): |
|
23 |
return True |
|
24 |
def first(self): |
|
25 |
return MockSAMLUser() |
|
26 |
if samlized: |
|
27 |
self.saml_identifiers = MockSAMLUsers() |
|
18 |
self.samlized = samlized |
|
19 | ||
20 |
def get_name_id(self): |
|
21 |
if self.samlized: |
|
22 |
return 'r2&d2' |
|
23 |
return None |
|
28 | 24 | |
29 | 25 | |
30 | 26 |
def test_crypto_url(): |
tests/test_wcs.py | ||
---|---|---|
161 | 161 |
WCS_DIR = tempfile.mkdtemp() |
162 | 162 |
WCS_PID = None |
163 | 163 | |
164 | ||
165 |
class MockUser(object): |
|
166 |
email = 'foo@example.net' |
|
167 |
def is_authenticated(self): |
|
168 |
return True |
|
169 |
def get_name_id(self): |
|
170 |
return None |
|
171 | ||
164 | 172 |
def run_wcs_script(script, hostname): |
165 | 173 |
script_path = os.path.join(WCS_DIR, script + '.py') |
166 | 174 |
fd = open(script_path, 'w') |
... | ... | |
345 | 353 |
cell = WcsCurrentFormsCell(page=page, placeholder='content', order=0) |
346 | 354 |
cell.save() |
347 | 355 | |
348 |
class MockUser(object): |
|
349 |
email = 'foo@example.net' |
|
350 |
def is_authenticated(self): |
|
351 |
return True |
|
352 | 356 |
context['request'].user = MockUser() |
353 | 357 | |
354 | 358 |
# query should fail as nothing is cached |
... | ... | |
412 | 416 |
cell.wcs_site = 'default' |
413 | 417 |
cell.save() |
414 | 418 | |
415 |
class MockUser(object): |
|
416 |
email = 'foo@example.net' |
|
417 |
def is_authenticated(self): |
|
418 |
return True |
|
419 | 419 |
context['request'].user = MockUser() |
420 | 420 | |
421 | 421 |
# query should fail as nothing is cached |
... | ... | |
506 | 506 |
cell.save() |
507 | 507 |
context['synchronous'] = True # to get fresh content |
508 | 508 | |
509 |
class MockUser(object): |
|
510 |
email = 'foo@example.net' |
|
511 |
def is_authenticated(self): |
|
512 |
return True |
|
513 | 509 |
context['request'].user = MockUser() |
514 | 510 | |
515 | 511 |
# default is to get current forms from all wcs sites |
... | ... | |
695 | 691 |
result = cell.render(context) |
696 | 692 |
assert '/backoffice/submission/a-private-form/' not in result |
697 | 693 | |
698 |
class MockUser(object): |
|
699 |
email = 'foo@example.net' |
|
700 |
def is_authenticated(self): |
|
701 |
return True |
|
702 | 694 |
context['request'].user = MockUser() |
703 | 695 | |
704 | 696 |
result = cell.render(context) |
705 | 697 |
assert '/backoffice/submission/a-private-form/' not in result |
706 | 698 | |
707 |
class MockUser(object):
|
|
699 |
class MockUser2(MockUser):
|
|
708 | 700 |
email = 'foo2@example.net' |
709 |
def is_authenticated(self): |
|
710 |
return True |
|
711 |
context['request'].user = MockUser() |
|
701 |
context['request'].user = MockUser2() |
|
712 | 702 | |
713 | 703 |
result = cell.render(context) |
714 | 704 |
assert '/backoffice/submission/a-private-form/' in result |
715 |
- |