Projet

Général

Profil

Télécharger (11 ko) Statistiques
| Branche: | Tag: | Révision:

mandayejs / tests / test_mandayejs.py @ b69ac82e

1
# -*- coding: utf-8 -*-
2

    
3
import json
4
import time
5
import mock
6
import os
7
import pytest
8

    
9
from django.conf import settings
10
from django.core.management import call_command
11
from django.http.request import HttpRequest, QueryDict
12
from django.forms.fields import DateField
13
from django.test.client import RequestFactory, Client
14
from django.core.urlresolvers import reverse
15

    
16
from mandayejs.mandaye.models import UserCredentials
17
from mandayejs.mandaye.utils import exec_phantom, get_login_info
18
from mandayejs.mandaye.forms import FormFactory
19
from mandayejs.mandaye.views import post_login_do
20

    
21

    
22
pytestmark = pytest.mark.django_db
23

    
24

    
25
LOGIN_INFO = {
26
    "address": "https://whatever.com",
27
    "auth_checker": "tenants/static/js/auth.checker.js",
28
    "cookies": [],
29
    "form_submit_element": "input[type=submit], button",
30
    "locators": [
31
        {
32
            "#username": "myusername",
33
            "#password": "mypassoword"
34
        }
35
    ]
36
}
37

    
38
MOCKED_SITE_LOCATORS = [
39
    {'id': '#login',
40
        'label': 'Login',
41
        'name': 'login',
42
        'kind': 'string', },
43
    {'id': '#password',
44
        'label': 'Password',
45
        'name': 'password',
46
        'kind': 'password'},
47
    {'id': '#birth_date',
48
        'label': 'Birthdata',
49
        'name': 'birth_date',
50
        'kind': 'date'}]
51

    
52

    
53
class MockedPopen(mock.Mock):
54
    stall_for = False
55

    
56
    def communicate(self, data):
57
        if self.stall_for:
58
            time.sleep(13)
59
        return self.expected_output
60

    
61

    
62
# ENCRYPTION/DECRYPTION
63
def test_encryption(credentials):
64
    decrypted = credentials.decrypt()
65
    assert decrypted.get('password') == u'jôhn password'
66

    
67

    
68
@pytest.fixture(params=['command_ldap', 'command_csv'])
69
def command(request, command_ldap, command_csv):
70
    return locals().get(request.param)
71

    
72

    
73
@mock.patch('mandayejs.mandaye.management.commands.migrate-users.get_idps')
74
def test_command_migrate_users(mocked_idps, command):
75
    mocked_idps.return_value = iter(settings.MELLON_IDENTITY_PROVIDERS)
76
    call_command(command.name, *command.args, **command.opts)
77
    if command.opts.get('ldap'):
78
        credentials = UserCredentials.objects.filter(user__last_name__in=[
79
            'ldap_user1',
80
            'ldap_user2',
81
            'ldap_user3'])
82

    
83
        assert len(credentials) == 3
84

    
85
        for cred in credentials:
86
            assert cred.to_login_info(decrypt=True)['#password'] == 'password_{}'.format(cred.user.last_name)
87
    else:
88
        credentials = UserCredentials.objects.all().exclude(user__last_name__in=[
89
            'ldap_user1',
90
            'ldap_user2',
91
            'ldap_user3'
92
        ])
93

    
94
        assert len(credentials) == 4
95

    
96
        for cred in credentials:
97
            assert cred.to_login_info(decrypt=True)['#password'] == cred.to_login_info()['#username']
98

    
99

    
100
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
101
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
102
def test_phantom_invalid_json(mocked_popen, caplog, user_john):
103
    expected_output = ('This is not a valid JSON', None)
104
    mocked_popen.return_value = MockedPopen(expected_output=expected_output)
105

    
106
    UserCredentials.objects.create(user=user_john,
107
                                   locators={
108
                                       'login': 'johnny', 'password': 'jumper',
109
                                       'birth_date': '1995-06-11'})
110

    
111
    client = Client()
112
    client.login(username='john', password='john')
113
    response = client.get(reverse('post-login-do'))
114
    assert 'window.top.location = "/_mandaye/associate/"' in response.content
115

    
116
    for message in response.context['messages']:
117
        assert message.level_tag == 'error'
118
        assert message.message == 'invalid response from server'
119

    
120
    for record in caplog.records:
121
        if record.levelname == 'ERROR':
122
            assert record.message == 'invalid json: This is not a valid JSON'
123

    
124

    
125
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
126
def test_phantom_js_errors(mocked_popen, caplog):
127
    stdout = {
128
        "stderr": "ERROR: TypeError: undefined is not a function (evaluating \'$( \"#datepicker\" )",
129
        "url": "https://whatever.com/someurl",
130
        "result": "ok"
131
    }
132

    
133
    expected_output = ('<mandayejs>%s</mandayejs>' % json.dumps(stdout), None)
134

    
135
    mocked_popen.return_value = MockedPopen(expected_output=expected_output)
136
    result = exec_phantom(LOGIN_INFO)
137

    
138
    for record in caplog.records:
139
        assert record.levelname == 'WARNING'
140
        assert record.message == "ERROR: TypeError: undefined is not a function (evaluating \'$( \"#datepicker\" )"
141

    
142
    assert result['result'] == 'ok'
143
    assert result['url'] == 'https://whatever.com/someurl'
144

    
145

    
146
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
147
def test_phantom_js_timeout(mocked_popen, caplog):
148
    mocked_popen.return_value = MockedPopen(expected_output=json.dumps({'whatever': 'whatever'}), stall_for=True)
149
    result = exec_phantom(LOGIN_INFO)
150

    
151
    for record in caplog.records:
152
        assert record.levelname == 'ERROR'
153
        assert 'https://whatever.com' in record.message
154
        assert 'tenants/static/js/auth.checker.js' in record.message
155
        assert 'input[type=submit], button' in record.message
156

    
157
    assert result['result'] == 'timeout'
158

    
159

    
160
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
161
def test_credentials_json_encoding(user_john):
162

    
163
    request = HttpRequest()
164
    request.POST = QueryDict('', mutable=True)
165

    
166
    formdata = {
167
        'login': 'johnny',
168
        'password': 'jumper',
169
        'birth_date': '1995-06-11'
170
    }
171

    
172
    request.POST.update(formdata)
173

    
174
    form = FormFactory(request.POST)
175
    form.is_valid()
176

    
177
    assert isinstance(form.fields['birth_date'], DateField) is True
178

    
179
    UserCredentials.objects.create(user=user_john, locators=form.cleaned_data)
180

    
181
    cred = UserCredentials.objects.get(user=user_john)
182

    
183
    assert cred.locators['login'] == 'johnny'
184
    assert cred.locators['birth_date'] == '1995-06-11'
185

    
186

    
187
@pytest.fixture(params=[
188
    {'url1': 'http://mydomain.com/update_password.aspx'},
189
    {'url2': 'http://mydomain.com/index?path=change_pass'}])
190
def redirect_url(request):
191
    return request.param
192

    
193

    
194
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
195
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
196
def test_password_redirection(mocked_popen, user_john, redirect_url):
197
    expected_output = {
198
        "result": "redirect",
199
        "reason": "password change required",
200
        "url": redirect_url.get('url1') or redirect_url.get('url2')
201
    }
202
    expected_output = '<mandayejs>%s</mandayejs>' % json.dumps(expected_output)
203
    mocked_popen.return_value = MockedPopen(expected_output=(expected_output, None))
204

    
205
    UserCredentials.objects.create(user=user_john,
206
                                   locators={
207
                                       'login': 'johnny', 'password': 'jumper',
208
                                       'birth_date': '1995-06-11'})
209

    
210
    request = RequestFactory()
211
    request = request.get(reverse('post-login-do'))
212
    request.user = user_john
213
    response = post_login_do(request)
214
    if 'url1' in redirect_url:
215
        assert 'window.top.location = "/update_password.aspx"' in response.content
216
    else:
217
        assert 'window.top.location = "/index?path=change_pass"' in response.content
218

    
219

    
220
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
221
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
222
def test_enclosed_response(mocked_popen):
223
    output = """<mandayejs>{"result": "ok",
224
    "authentication": "success"} </mandayejs>
225
    this is just a random error"""
226

    
227
    mocked_popen.return_value = MockedPopen(expected_output=(output, None))
228
    result = exec_phantom(LOGIN_INFO)
229
    assert result['result'] == 'ok'
230

    
231
    # with no match
232
    mocked_popen.return_value = MockedPopen(expected_output=('<mandayejs></mandayejs>', None))
233
    result = exec_phantom(LOGIN_INFO)
234
    assert result['result'] == 'json_error'
235

    
236

    
237
@mock.patch('mandayejs.mandaye.utils.subprocess.Popen')
238
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
239
def test_post_login_do_with_next_url(mocked_popen, user_john):
240
    # when sso fails
241
    expected_output = {
242
        "result": "redirect",
243
        "reason": "password change required",
244
        "url": "http://mydomain.com/update_password.aspx"
245
    }
246
    expected_output = '<mandayejs>%s</mandayejs>' % json.dumps(expected_output)
247
    mocked_popen.return_value = MockedPopen(expected_output=(expected_output, None))
248

    
249
    UserCredentials.objects.create(user=user_john,
250
                                   locators={
251
                                       'login': 'johnny', 'password': 'jumper',
252
                                       'birth_date': '1995-06-11'})
253

    
254
    request = RequestFactory()
255
    url = '%s?next=http://example.net/' % reverse('post-login-do')
256
    request = request.get(url)
257
    request.user = user_john
258
    response = post_login_do(request)
259
    assert 'window.top.location = "http://example.net/"' not in response.content
260

    
261
    # when SSO succeeds
262
    expected_output = {
263
        "result": "ok",
264
        "url": "http://mydomain.com/account.aspx"
265
    }
266
    expected_output = '<mandayejs>%s</mandayejs>' % json.dumps(expected_output)
267
    mocked_popen.return_value = MockedPopen(expected_output=(expected_output, None))
268
    request = RequestFactory()
269
    url = '%s?next_url=http://example.net/' % reverse('post-login-do')
270
    request = request.get(url)
271
    request.user = user_john
272
    response = post_login_do(request)
273
    assert 'window.top.location = "http://example.net/"' in response.content
274

    
275

    
276
@mock.patch('mandayejs.applications.Test.SITE_LOCATORS', MOCKED_SITE_LOCATORS)
277
def test_app_settings_overriding(settings, cred_john):
278
    request = RequestFactory().get('/')
279
    data = get_login_info(request, cred_john)
280
    assert data['address'] == 'http://testserver/'
281
    assert data['auth_checker'] == os.path.join(settings.STATIC_ROOT, 'js/test/auth.checker.js')
282
    assert data['form_submit_element'] == 'input[type=submit], button'
283
    # when overriding settings
284
    settings.SITE_LOGIN_PATH = 'account/login'
285
    settings.SITE_AUTH_CHECKER = 'js/global.auth.checker.js'
286
    settings.SITE_FORM_SUBMIT_ELEMENT = 'button'
287
    data = get_login_info(request, cred_john)
288
    assert data['address'] == 'http://testserver/account/login'
289
    assert data['auth_checker'] == os.path.join(settings.STATIC_ROOT, 'js/global.auth.checker.js')
290
    assert data['form_submit_element'] == 'button'
291
    settings.SITE_LOGIN_PATH = ''
292
    settings.SITE_FORM_SUBMIT_ELEMENT = ''
293
    data = get_login_info(request, cred_john)
294
    assert data['address'] == 'http://testserver/'
295
    assert data['form_submit_element'] == ''
296

    
297

    
298
def test_app_name_slug_in_association_context(settings, user_john):
299
    client = Client()
300
    client.login(username='john', password='john')
301
    response = client.get(reverse('associate'))
302
    assert response.context['app']['name'] == 'Test'
303
    assert response.context['app']['slug'] == 'test'
304
    # when overidding the appname in settings
305
    settings.SITE_APP_NAME = 'Test A'
306
    response = client.get(reverse('associate'))
307
    assert response.context['app']['name'] == 'Test A'
308
    assert response.context['app']['slug'] == 'test'
(7-7/8)