Project

General

Profile

0002-matomo-manage-matomo-s-webservices-19743.patch

Nicolas Roche, 27 March 2019 06:08 PM

Download (31.7 KB)

View differences:

Subject: [PATCH 2/4] matomo: manage matomo's webservices (#19743)

 hobo/matomo/README         |  14 +
 hobo/matomo/utils.py       | 253 ++++++++++++++++
 requirements.txt           |   1 +
 tests/test_matomo_utils.py | 596 +++++++++++++++++++++++++++++++++++++
 tox.ini                    |   1 +
 5 files changed, 865 insertions(+)
 create mode 100644 hobo/matomo/README
 create mode 100644 hobo/matomo/utils.py
 create mode 100644 tests/test_matomo_utils.py
hobo/matomo/README
1

  
2
Il faut renseigner quelques variables pour pouvoir profiter de
3
la configuration automatique de Matomo :
4
- l'url d'un serveur matomo
5
- le login et mot de passe d'un administrateur du serveur matomo
6

  
7
Par exemple chez Entr'ouvert :
8

  
9
/var/lib/hobo/tenants/hobo.dev.publik.love/settings.json:
10
{
11
  "MATOMO_FEED": {
12
    "URL": "https://matomo-test.entrouvert.org"
13
    "TOKEN_AUTH": "xxx"
14
}
hobo/matomo/utils.py
1
# hobo - portal to configure and deploy applications
2
# Copyright (C) 2015  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
import requests
18
import string
19

  
20
from lxml import etree
21
from random import *
22

  
23
from django.db import connection
24
from django.conf import settings
25
from django.utils.six.moves.urllib import parse as urlparse
26

  
27
from hobo.environment.models import Variable, Wcs, Combo, Fargo
28

  
29

  
30
def get_variable(name):
31
    """get hobo variables from DB"""
32
    variable, created = Variable.objects.get_or_create(
33
        name=name,
34
        defaults={'auto': True, 'value': ''})
35
    return variable
36

  
37
def get_matomo_settings():
38
    """get hobo configuration from settings"""
39
    config = getattr(settings, 'MATOMO_FEED', {})
40
    try:
41
        url_ws_base = config['URL']
42
        token_auth = config['TOKEN_AUTH']
43
    except KeyError as exc:
44
        raise MatomoError('no settings for matomo: %s' % str(exc))
45
    return url_ws_base, token_auth
46

  
47
def get_tenant_name_and_public_urls():
48
    """get an alias for our matomo's id and urls to monitor"""
49
    tenant_name = None
50
    services = [x for x in Combo.objects.all() if x.template_name == 'portal-user']
51
    if services != [] and services[0] != '':
52
        tenant_name = urlparse.urlparse(services[0].base_url).netloc
53
    services += [x for x in Wcs.objects.all()]
54
    services += [x for x in Fargo.objects.all()]
55
    site_urls = [x.base_url for x in services if x.base_url != '']
56
    return tenant_name, site_urls
57

  
58
class MatomoException(Exception):
59
    """unexpected Matomo internal error"""
60

  
61
class MatomoError(MatomoException):
62
    """expected Matomo error responses"""
63

  
64
class MatomoWS(object):
65
    """api for matomo webservices"""
66

  
67
    def __init__(self, url, token_auth):
68
        self.url_ws_base = url
69
        self.token_auth = token_auth
70

  
71
    @staticmethod
72
    def _parse_response(content):
73
        try:
74
            tree = etree.fromstring(content)
75
        except etree.XMLSyntaxError as exc:
76
            raise MatomoException('etree.XMLSyntaxError: %s' % str(exc))
77
        return tree
78

  
79
    @staticmethod
80
    def _raise_on_error(tree):
81
        """handle matomo XML error messages"""
82
        tags = tree.xpath('/result/error')
83
        if tags != []:
84
            try:
85
                attr = tags[0].items()[0]
86
                if attr[0] == 'message':
87
                    raise MatomoError(attr[1])
88
            except IndexError:
89
                pass
90
            raise MatomoException('internal error')
91

  
92
    def call(self, data):
93
        data['module'] = 'API'
94
        data['token_auth'] = self.token_auth
95
        resp = requests.post(self.url_ws_base, data=data)
96
        tree = self._parse_response(resp.content)
97
        self._raise_on_error(tree)
98
        return tree
99

  
100
    def get_site_from_id(self, id_site):
101
        data = {'method': 'SitesManager.getSiteFromId',
102
                'idSite': id_site}
103
        tree = self.call(data)
104
        try:
105
            tag = tree.xpath('/result/row/idsite')[0]
106
        except IndexError:
107
            raise MatomoException('get_site_from_id fails')
108
        return tag.text
109

  
110
    def add_site(self, site_name, site_urls):
111
        data = {'method': 'SitesManager.addSite',
112
                'siteName': site_name}
113
        cpt = 0
114
        for url in site_urls:
115
            key = 'urls[%i]' % cpt
116
            data[key] = url
117
            cpt += 1
118
        tree = self.call(data)
119
        try:
120
            tag = tree.xpath('/result')[0]
121
        except IndexError:
122
            raise MatomoException('add_site fails')
123
        return tag.text
124

  
125
    def add_user(self, user_login, password, email, alias='', initial_id_site=''):
126
        data = {'method': 'UsersManager.addUser',
127
                'userLogin': user_login,
128
                'password': password,
129
                'email': email,
130
                'alias': alias,
131
                'initialIdSite': initial_id_site}
132
        tree = self.call(data)
133
        success = True
134
        tags = tree.xpath('/result/success')
135
        if tags != []:
136
            try:
137
                attr = tags[0].items()[0]
138
                if attr[0] != 'message' or attr[1] != 'ok':
139
                    success = False
140
            except IndexError:
141
                success = False
142
        if not success:
143
            raise MatomoException('add_user fails')
144

  
145
    def get_javascript_tag(self, id_site):
146
        data = {'method': 'SitesManager.getJavascriptTag',
147
                'idSite': id_site}
148
        tree = self.call(data)
149
        try:
150
            tag = tree.xpath('/result')[0]
151
        except IndexError:
152
            raise MatomoException('get_javascript_tag fails')
153
        return tag.text
154

  
155
def is_site_already_added(matomo, id_site):
156
    # API does not allow to retrieve our site without using the matomo's id.
157
    try:
158
        id_site_resp = matomo.get_site_from_id(id_site)
159
    except MatomoError:
160
        return False
161
    return id_site_resp == id_site
162

  
163
def add_user_if_not_already_there(matomo, user_login, password, id_site):
164
    email = 'noreply_' + user_login + '@entrouvert.com'
165
    try:
166
        matomo.add_user(user_login, password, email,
167
                        alias='', initial_id_site=id_site)
168
    except MatomoError as exc:
169
        # not an error if user is already here
170
        if str(exc) != "Username '%s' already exists." % user_login:
171
            raise exc
172

  
173
def get_and_normalise_javascript_tag(matomo, id_site):
174
    CNIL_JS = """
175
  /* disallow cookie's time extension */
176
  _paq.push([function() {
177
    var self = this;
178
    function getOriginalVisitorCookieTimeout() {
179
      var now = new Date(),
180
      nowTs = Math.round(now.getTime() / 1000),
181
      visitorInfo = self.getVisitorInfo();
182
      var createTs = parseInt(visitorInfo[2]);
183
      var cookieTimeout = 33696000; // 13 mois en secondes
184
      var originalTimeout = createTs + cookieTimeout - nowTs;
185
      return originalTimeout;
186
    }
187
    this.setVisitorCookieTimeout( getOriginalVisitorCookieTimeout() );
188
  }]);
189
"""
190
    matomo_tag = matomo.get_javascript_tag(id_site)
191
    enhanced_tag = matomo_tag.split('\n')
192

  
193
    # acording to publik-base-theme/templates/includes/tracking.html,
194
    # we need to remove <script> tags from matomo's output javascript.
195
    del enhanced_tag[1]
196
    del enhanced_tag[-3]
197

  
198
    # disallow cookie's time extension
199
    enhanced_tag.insert(2, CNIL_JS)
200

  
201
    enhanced_tag = '\n'.join(enhanced_tag)
202
    return enhanced_tag
203

  
204
def compute_cnil_acknowledgment_level(tracking_js):
205
    if tracking_js.find('google') != -1:
206
        # google reference found into javascript
207
        return 'error'
208
    if tracking_js.find('getOriginalVisitorCookieTimeout') == -1:
209
        # can't find cookie's life time extension prevention
210
        return 'warning'
211
    return 'success'
212

  
213
def auto_configure_matomo():
214
    """main function"""
215

  
216
    # retrieve matomo's variables
217
    id_site_var = get_variable('matomo_id_site')
218
    password_var = get_variable('matomo_password')
219
    tracking_js_var = get_variable('cnil_compliant_visits_tracking_js')
220
    error_message_var = get_variable('matomo_error')
221

  
222
    id_site = id_site_var.value
223
    password = password_var.value
224
    if password_var.value == '':
225
        characters = string.ascii_letters + string.punctuation  + string.digits
226
        password = "".join(choice(characters) for x in range(randint(8, 16)))
227

  
228
    try:
229
        tenant_name, site_urls = get_tenant_name_and_public_urls()
230
        if tenant_name is None:
231
            raise MatomoException("no portal-user url available for a matomo's id")
232

  
233
        matomo = MatomoWS(*get_matomo_settings())
234
        if not is_site_already_added(matomo, id_site):
235
            id_site = matomo.add_site(tenant_name, site_urls)
236
        add_user_if_not_already_there(matomo, tenant_name, password, id_site)
237
        javascript_tag = get_and_normalise_javascript_tag(matomo, id_site)
238

  
239
    except MatomoException as exc:
240
        error_message_var.value = str(exc)
241
        error_message_var.save()
242
        return False
243

  
244
    # save matomo's variables
245
    id_site_var.value = id_site
246
    password_var.value = password
247
    tracking_js_var.value = javascript_tag
248
    error_message_var.value = ''
249
    id_site_var.save()
250
    password_var.save()
251
    tracking_js_var.save()
252
    error_message_var.save()
253
    return True
requirements.txt
2 2
-e git+http://repos.entrouvert.org/gadjo.git/#egg=gadjo
3 3
celery<4
4 4
django-mellon
5
lxml
5 6
prometheus_client
tests/test_matomo_utils.py
1
# -*- coding: utf-8 -*-
2

  
3
import mock
4
import pytest
5
from lxml import etree
6
from requests import Response
7

  
8
from django.test import override_settings
9

  
10
from hobo.environment.models import Variable, Wcs, Combo, Fargo
11
from hobo.matomo.utils import MatomoError, MatomoException, MatomoWS, \
12
       get_variable, get_matomo_settings, get_tenant_name_and_public_urls, \
13
       is_site_already_added, add_user_if_not_already_there, \
14
       get_and_normalise_javascript_tag, compute_cnil_acknowledgment_level, \
15
       auto_configure_matomo
16

  
17
pytestmark = pytest.mark.django_db
18

  
19
CONFIG = {'URL': 'https://matomo.test', 'TOKEN_AUTH': '1234'}
20

  
21
GET_SITE_42_ALREADY_THERE = """<?xml version="1.0" encoding="utf-8" ?>
22
<result>
23
    <row>
24
        <idsite>42</idsite>
25
        <moretags>...</moretags>
26
    </row>
27
</result>
28
"""
29

  
30
GET_SITE_34_ALREADY_THERE = """<?xml version="1.0" encoding="utf-8" ?>
31
<result>
32
    <row>
33
        <idsite>34</idsite>
34
        <moretags>...</moretags>
35
    </row>
36
</result>
37
"""
38

  
39
GET_SITE_NO_ID_PROVIDED = """<?xml version="1.0" encoding="utf-8" ?>
40
<result>
41
        <error message="Please specify a value for 'idSite'." />
42
</result>
43
"""
44

  
45
GET_SITE_BAD_RESPONSE = """<?xml version="1.0" encoding="utf-8" ?>
46
<result>
47
    <row>
48
        <not_idsite>there is no idsite tag</not_idsite>
49
        <moretags>...</moretags>
50
    </row>
51
</result>
52
"""
53

  
54
GET_SITE_NOT_ALREADY_THERE = """<?xml version="1.0" encoding="utf-8" ?>
55
<result>
56
    <error message="An unexpected website was found in the request: website id was set to '42' ." />
57
</result>
58
"""
59

  
60
ADD_SITE_SUCCESS = """<?xml version="1.0" encoding="utf-8" ?>
61
<result>42</result>
62
"""
63

  
64
ADD_SITE_ERROR = """<?xml version="1.0" encoding="utf-8" ?>
65
<result>
66
        <error message="Please specify a value for 'siteName'." />
67
</result>
68
"""
69

  
70
ADD_SITE_BAD_RESPONSE = """<?xml version="1.0" encoding="utf-8" ?>
71
<not_result>no result tag</not_result>
72
"""
73

  
74
ADD_USER_SUCCESS = """<?xml version="1.0" encoding="utf-8" ?>
75
<result>
76
    <success message="ok" />
77
</result>
78
"""
79

  
80
USER_ALREADY_THERE = """<?xml version="1.0" encoding="utf-8" ?>
81
<result>
82
    <error message="Username 'hobo_dev_publik_love' already exists." />
83
</result>"""
84

  
85
MAIL_ALREADY_THERE = """<?xml version="1.0" encoding="utf-8" ?>
86
<result>
87
    <error message="User with email 'nestor@testor.org' already exists." />
88
</result>"""
89

  
90
BAD_CREDENTIAL = """<?xml version="1.0" encoding="utf-8" ?>
91
<result>
92
    <error message="You can\'t access this resource as it requires a \'superuser\' access." />
93
</result>"""
94

  
95
ADD_USER_BAD_RESPONSE_1 = """<?xml version="1.0" encoding="utf-8" ?>
96
<result>
97
    <success message="KO" />
98
</result>
99
"""
100

  
101
ADD_USER_BAD_RESPONSE_2 = """<?xml version="1.0" encoding="utf-8" ?>
102
<result>
103
    <success>no message attribute</success>
104
    <not_success>no success tag</not_success>
105
</result>
106
"""
107

  
108
JAVASCRIPT_TAG = """<?xml version="1.0" encoding="utf-8" ?>
109
<result>&lt;!-- Matomo --&gt;
110
&lt;script type=&quot;text/javascript&quot;&gt;
111
  var _paq = window._paq || [];
112
  /* tracker methods like &quot;setCustomDimension&quot; should be called before &quot;trackPageView&quot; */
113
  _paq.push(['trackPageView']);
114
  _paq.push(['enableLinkTracking']);
115
  (function() {
116
    var u=&quot;//matomo-test.entrouvert.org/&quot;;
117
    _paq.push(['setTrackerUrl', u+'piwik.php']);
118
    _paq.push(['setSiteId', '7']);
119
    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
120
    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
121
  })();
122
&lt;/script&gt;
123
&lt;!-- End Matomo Code --&gt;
124
</result>
125
"""
126

  
127
BAS_CREDENTIALS = """<?xml version="1.0" encoding="utf-8" ?>
128
<result>
129
        <error message="You can't access this resource as it requires 'view' access for the website id = 42." />
130
</result>
131
"""
132

  
133
JAVASCRIPT_TAG_BAD_RESPONSE = """<?xml version="1.0" encoding="utf-8" ?>
134
<no_result_tag/>
135
"""
136

  
137
def test_get_variable():
138
    """hobo variables from DB"""
139
    Variable.objects.create(name='matomo_id_site', value='42')
140

  
141
    id_site_var = get_variable('matomo_id_site')
142
    assert id_site_var.value == '42'
143

  
144
def test_matomo_settings():
145
    """hobo configuration from settings"""
146
    with override_settings(MATOMO_FEED=CONFIG):
147
        url, token = get_matomo_settings()
148
        assert url == 'https://matomo.test'
149
        assert token == '1234'
150

  
151
    with override_settings(MATOMO_FEED={}):
152
        try:
153
            url, token = get_matomo_settings()
154
        except MatomoError as exc:
155
            assert str(exc) == "no settings for matomo: 'URL'"
156
        else:
157
            assert False
158

  
159
def test_get_tenant_name_and_public_urls():
160
    Combo.objects.create(base_url='https://combo.dev.publik.love',
161
                         template_name='portal-user')
162
    Wcs.objects.create(base_url='https://wcs.dev.publik.love')
163
    Fargo.objects.create(base_url='https://fargo.dev.publik.love')
164
    tenant_name, site_urls = get_tenant_name_and_public_urls()
165
    assert tenant_name == 'combo.dev.publik.love'
166
    assert site_urls[2] == 'https://fargo.dev.publik.love/'
167

  
168
def test_matomo_constructor():
169
    """build the matomo webservice object"""
170
    with override_settings(MATOMO_FEED=CONFIG):
171
        matomo = MatomoWS(*get_matomo_settings())
172
        assert matomo.url_ws_base == 'https://matomo.test'
173
        assert matomo.token_auth == '1234'
174

  
175
    with override_settings(MATOMO_FEED={}):
176
        try:
177
            matomo = MatomoWS(*get_matomo_settings())
178
        except MatomoError as exc:
179
            assert str(exc) == "no settings for matomo: 'URL'"
180
        else:
181
            assert False
182

  
183
def test_parse_response():
184
    """parser used by all matomo webservice calls"""
185
    with override_settings(MATOMO_FEED=CONFIG):
186
        matomo = MatomoWS(*get_matomo_settings())
187

  
188
        # no error (expected format)
189
        content = """<?xml version="1.0" encoding="utf-8" ?><ok/>"""
190
        tree = matomo._parse_response(content)
191
        assert tree.tag == 'ok'
192

  
193
        # error (not XML format)
194
        content = """this is not XML"""
195
        try:
196
            tree = matomo._parse_response(content)
197
        except MatomoException as exc:
198
            assert str(exc).find("XMLSyntaxError: Start tag expected") != -1
199
        else:
200
            assert False
201

  
202
def test_get_error_message():
203
    """error handler used by all matomo webservice calls"""
204
    with override_settings(MATOMO_FEED=CONFIG):
205
        matomo = MatomoWS(*get_matomo_settings())
206

  
207
        # no error (expected format)
208
        content = """<?xml version="1.0" encoding="utf-8" ?><ok/>"""
209
        tree = matomo._parse_response(content)
210
        matomo._raise_on_error(tree)
211
        assert tree.tag == 'ok'
212

  
213
        # error (expected format)
214
        content = """<?xml version="1.0" encoding="utf-8" ?>
215
<result>
216
    <error message="here is the error message" />
217
</result>
218
"""
219
        tree = matomo._parse_response(content)
220
        try:
221
            matomo._raise_on_error(tree)
222
        except MatomoError as exc:
223
            assert str(exc) == 'here is the error message'
224
        else:
225
            assert False
226

  
227
        # error (unexpected format)
228
        content = """<?xml version="1.0" encoding="utf-8" ?>
229
<result>
230
    <error>no 'message' attribute here</error>
231
</result>
232
"""
233
        tree = matomo._parse_response(content)
234
        try:
235
            matomo._raise_on_error(tree)
236
        except MatomoException as exc:
237
            assert str(exc) == 'internal error'
238
        else:
239
            assert False
240

  
241
@mock.patch('requests.post')
242
def test_get_site_from_id(mocked_post):
243
    """webservice to test if the site is already registered"""
244
    with override_settings(MATOMO_FEED=CONFIG):
245
        matomo = MatomoWS(*get_matomo_settings())
246

  
247
        # site already here
248
        content = GET_SITE_42_ALREADY_THERE
249
        mocked_post.return_value.content = content
250
        assert matomo.get_site_from_id('42') == '42'
251

  
252
        # site not already here
253
        content = GET_SITE_NOT_ALREADY_THERE
254
        mocked_post.return_value.content = content
255
        try:
256
            matomo.get_site_from_id(42)
257
        except MatomoError as exc:
258
            assert str(exc).find('An unexpected website was found in ') != -1
259
        else:
260
            assert False
261

  
262
        # error on empty id
263
        content = GET_SITE_NO_ID_PROVIDED
264
        mocked_post.return_value.content = content
265
        try:
266
            matomo.get_site_from_id(42)
267
        except MatomoError as exc:
268
            assert str(exc) == "Please specify a value for 'idSite'."
269
        else:
270
            assert False
271

  
272
        # bad response (error on success response)
273
        content = GET_SITE_BAD_RESPONSE
274
        mocked_post.return_value.content = content
275
        try:
276
            matomo.get_site_from_id(42)
277
        except MatomoException as exc:
278
            assert str(exc) == 'get_site_from_id fails'
279
        else:
280
            assert False
281

  
282
@mock.patch('requests.post')
283
def test_is_site_already_added(mocked_post):
284
    """function to test if the site is already regisered"""
285
    with override_settings(MATOMO_FEED=CONFIG):
286
        matomo = MatomoWS(*get_matomo_settings())
287

  
288
        # site already here
289
        content = GET_SITE_42_ALREADY_THERE
290
        mocked_post.return_value.content = content
291
        assert is_site_already_added(matomo, '42') is True
292

  
293
        # site not already here
294
        content = GET_SITE_NOT_ALREADY_THERE
295
        mocked_post.return_value.content = content
296
        assert is_site_already_added(matomo, '42') is False
297

  
298
        # empty id provided
299
        content = GET_SITE_NO_ID_PROVIDED
300
        mocked_post.return_value.content = content
301
        assert is_site_already_added(matomo, '42') is False
302

  
303
        # strange case
304
        content = GET_SITE_34_ALREADY_THERE
305
        mocked_post.return_value.content = content
306
        assert is_site_already_added(matomo, '42') is False
307

  
308
        # error
309
        content = GET_SITE_BAD_RESPONSE
310
        mocked_post.return_value.content = content
311
        try:
312
            is_site_already_added(matomo, '42')
313
        except MatomoException as exc:
314
            assert str(exc) == 'get_site_from_id fails'
315
        else:
316
            assert False
317

  
318
@mock.patch('requests.post')
319
def test_add_site(mocked_post):
320
    """webservice to add a new site"""
321
    urls = ['https://combo.dev.publik.love',
322
            'https://wcs.dev.publik.love']
323
    with override_settings(MATOMO_FEED=CONFIG):
324
        matomo = MatomoWS(*get_matomo_settings())
325

  
326
        # success
327
        content = ADD_SITE_SUCCESS
328
        mocked_post.return_value.content = content
329
        site_id = matomo.add_site("hobo_dev_publik_love", urls)
330
        assert site_id == '42'
331

  
332
        # error
333
        content = ADD_SITE_ERROR
334
        mocked_post.return_value.content = content
335
        try:
336
            site_id = matomo.add_site("hobo_dev_publik_love", urls)
337
        except MatomoError as exc:
338
            assert str(exc) == "Please specify a value for 'siteName'."
339
        else:
340
            assert False
341

  
342
        # strange message
343
        content = ADD_SITE_BAD_RESPONSE
344
        mocked_post.return_value.content = content
345
        try:
346
            site_id = matomo.add_site("hobo_dev_publik_love", urls)
347
        except MatomoException as exc:
348
            assert str(exc) == 'add_site fails'
349
        else:
350
            assert False
351

  
352
@mock.patch('requests.post')
353
def test_add_user(mocked_post):
354
    """webservice to add new user"""
355
    with override_settings(MATOMO_FEED=CONFIG):
356
        matomo = MatomoWS(*get_matomo_settings())
357

  
358
        # success
359
        content = ADD_USER_SUCCESS
360
        mocked_post.return_value.content = content
361
        matomo.add_user('nestor', 'xxx', 'nestor@testor.org')
362
        assert True
363

  
364
        # error (user already here)
365
        content = USER_ALREADY_THERE
366
        mocked_post.return_value.content = content
367
        try:
368
            matomo.add_user('hobo_dev_publik_love', 'xxx', 'nestor@testor.org')
369
        except MatomoError as exc:
370
            assert str(exc).find("Username 'hobo_dev_publik_love' already") != -1
371
        else:
372
            assert False
373

  
374
        # error (mail already registered)
375
        content = MAIL_ALREADY_THERE
376
        mocked_post.return_value.content = content
377
        try:
378
            matomo.add_user('nestor', 'xxx', 'nestor@testor.org')
379
        except MatomoError as exc:
380
            assert str(exc).find("User with email 'nestor@testor.org'") != -1
381
        else:
382
            assert False
383

  
384
        # error (bad credentials)
385
        content = BAD_CREDENTIAL
386
        mocked_post.return_value.content = content
387
        try:
388
            matomo.add_user('nestor', 'xxx', 'nestor@testor.org')
389
        except MatomoError as exc:
390
            assert str(exc).find("You can\'t access this resource") != -1
391
        else:
392
            assert False
393

  
394
        # bad success message (wrong attribute value)
395
        content = ADD_USER_BAD_RESPONSE_1
396
        mocked_post.return_value.content = content
397
        try:
398
            matomo.add_user('nestor', 'xxx', 'nestor@testor.org')
399
        except MatomoException as exc:
400
            assert str(exc) == 'add_user fails'
401
        else:
402
            assert False
403

  
404
       # bad success message (no message attribute)
405
        content = ADD_USER_BAD_RESPONSE_2
406
        mocked_post.return_value.content = content
407
        try:
408
            matomo.add_user('nestor', 'xxx', 'nestor@testor.org')
409
        except MatomoException as exc:
410
            assert str(exc) == 'add_user fails'
411
        else:
412
            assert False
413

  
414
@mock.patch('requests.post')
415
def test_add_user_if_not_already_there(mocked_post):
416
    """function to assert we have a user"""
417
    with override_settings(MATOMO_FEED=CONFIG):
418
        matomo = MatomoWS(*get_matomo_settings())
419

  
420
        # success (add a new user)
421
        content = ADD_USER_SUCCESS
422
        mocked_post.return_value.content = content
423
        add_user_if_not_already_there(matomo, 'hobo_dev_publik_love', 'xxx', '42')
424
        assert True
425

  
426
        # success (user already here)
427
        content = USER_ALREADY_THERE
428
        mocked_post.return_value.content = content
429
        add_user_if_not_already_there(matomo, 'hobo_dev_publik_love', 'xxx', '42')
430
        assert True
431

  
432
        # error (bad credentials)
433
        content = BAD_CREDENTIAL
434
        mocked_post.return_value.content = content
435
        try:
436
            add_user_if_not_already_there(matomo, 'tenant_name', 'xxx', '42')
437
        except MatomoError:
438
            assert True
439
        else:
440
            assert False
441

  
442
@mock.patch('requests.post')
443
def test_get_javascript_tag(mocked_post):
444
    """webservice to get matomo JS tag"""
445
    with override_settings(MATOMO_FEED=CONFIG):
446
        matomo = MatomoWS(*get_matomo_settings())
447

  
448
        # success
449
        content = JAVASCRIPT_TAG
450
        mocked_post.return_value.content = content
451
        javascript_tag = matomo.get_javascript_tag('42')
452
        assert javascript_tag.find('(function() {') != -1
453

  
454
        # error (bad credentials)
455
        content = BAD_CREDENTIAL
456
        mocked_post.return_value.content = content
457
        try:
458
            javascript_tag = matomo.get_javascript_tag('42')
459
        except MatomoError as exc:
460
            assert str(exc).find("You can't access this resource ") != -1
461
        else:
462
            assert False
463

  
464
       # bad response (no result tag)
465
        content = JAVASCRIPT_TAG_BAD_RESPONSE
466
        mocked_post.return_value.content = content
467
        try:
468
            javascript_tag = matomo.get_javascript_tag('42')
469
        except MatomoException as exc:
470
            assert str(exc) == 'get_javascript_tag fails'
471
        else:
472
            assert False
473

  
474
def test_compute_cnil_acknowledgment_level():
475
    """function use to inspect javascript content"""
476
    warning_content = JAVASCRIPT_TAG
477

  
478
    # can't find cookie's life time extension prevention
479
    assert compute_cnil_acknowledgment_level(warning_content) == 'warning'
480

  
481
    # ok
482
    success_content = warning_content + '\n...getOriginalVisitorCookieTimeout...'
483
    assert compute_cnil_acknowledgment_level(success_content) == 'success'
484

  
485
    # google reference found into javascript
486
    error_content = success_content + '\n...google...'
487
    assert compute_cnil_acknowledgment_level(error_content) == 'error'
488

  
489
@mock.patch('requests.post')
490
def test_get_and_normalise_javascript_tag(mocked_post):
491
    """function to get matomo JS tag"""
492
    with override_settings(MATOMO_FEED=CONFIG):
493
        matomo = MatomoWS(*get_matomo_settings())
494

  
495
        # success
496
        content = JAVASCRIPT_TAG
497
        mocked_post.return_value.content = content
498
        javascript_tag = get_and_normalise_javascript_tag(matomo, '42')
499
        print javascript_tag
500
        assert javascript_tag.find('(function() {') != -1
501
        assert javascript_tag.find('&lt;script') == -1
502
        assert javascript_tag.find('script&gt;') == -1
503
        assert compute_cnil_acknowledgment_level(javascript_tag) == 'success'
504

  
505
def auto_conf_mocked_post(url, **kwargs):
506
    contents = [GET_SITE_NO_ID_PROVIDED,
507
                ADD_SITE_SUCCESS,
508
                ADD_USER_SUCCESS,
509
                JAVASCRIPT_TAG]
510
    response = Response()
511
    response._content = contents[auto_conf_mocked_post.cpt]
512
    response.status_code = 200
513

  
514
    auto_conf_mocked_post.cpt += 1
515
    return response
516

  
517
@mock.patch('requests.post', side_effect=auto_conf_mocked_post)
518
def test_auto_configure_matomo(mocked_post):
519
    Variable.objects.create(name='matomo_id_site', value='42')
520
    Variable.objects.create(name='matomo_password', value='567')
521
    Variable.objects.create(name='tracking_js_var', value='js_code')
522
    Variable.objects.create(name='matomo_error', value='')
523

  
524
    Combo.objects.create(base_url='https://combo.dev.publik.love',
525
                         template_name='portal-user')
526
    Wcs.objects.create(base_url='https://wcs.dev.publik.love')
527
    Fargo.objects.create(base_url='https://fargo.dev.publik.love')
528

  
529
    auto_conf_mocked_post.cpt = 0
530
    with override_settings(MATOMO_FEED=CONFIG):
531
        assert auto_configure_matomo() is True
532

  
533
@mock.patch('requests.post', side_effect=auto_conf_mocked_post)
534
def test_auto_configure_matomo_no_url(mocked_post):
535
    Variable.objects.create(name='matomo_id_site', value='42')
536
    Variable.objects.create(name='matomo_password', value='567')
537
    Variable.objects.create(name='tracking_js_var', value='js_code')
538
    Variable.objects.create(name='matomo_error', value='')
539

  
540
    # no Wc url so as to raise
541
    Wcs.objects.create(base_url='https://wcs.dev.publik.love')
542
    Fargo.objects.create(base_url='https://fargo.dev.publik.love')
543
    
544
    auto_conf_mocked_post.cpt = 0
545
    with override_settings(MATOMO_FEED=CONFIG):
546
        assert auto_configure_matomo() is False
547
    message = Variable.objects.get(name='matomo_error').value
548
    assert message == "no portal-user url available for a matomo's id"
549

  
550
@mock.patch('requests.post', side_effect=auto_conf_mocked_post)
551
def test_auto_configure_matomo_no_password(mocked_post):
552
    Variable.objects.create(name='matomo_id_site', value='42')
553

  
554
    # force to generate new password
555
    Variable.objects.create(name='matomo_password', value='')
556
    Variable.objects.create(name='tracking_js_var', value='js_code')
557
    Variable.objects.create(name='matomo_error', value='')
558

  
559
    Combo.objects.create(base_url='https://combo.dev.publik.love',
560
                         template_name='portal-user')
561
    Wcs.objects.create(base_url='https://wcs.dev.publik.love')
562
    Fargo.objects.create(base_url='https://fargo.dev.publik.love')
563

  
564
    auto_conf_mocked_post.cpt = 0
565
    with override_settings(MATOMO_FEED=CONFIG):
566
        assert auto_configure_matomo() is True
567

  
568
def auto_conf_mocked_post_error(url, **kwargs):
569
    contents = [GET_SITE_NO_ID_PROVIDED,
570
                ADD_SITE_SUCCESS,
571
                ADD_USER_SUCCESS,
572
                JAVASCRIPT_TAG_BAD_RESPONSE]
573
    response = Response()
574
    response._content = contents[auto_conf_mocked_post.cpt]
575
    response.status_code = 200
576

  
577
    auto_conf_mocked_post.cpt += 1
578
    return response
579

  
580
@mock.patch('requests.post', side_effect=auto_conf_mocked_post_error)
581
def test_auto_configure_matomo_error(mocked_post):
582
    Variable.objects.create(name='matomo_id_site', value='42')
583
    Variable.objects.create(name='matomo_password', value='567')
584
    Variable.objects.create(name='tracking_js_var', value='js_code')
585
    Variable.objects.create(name='matomo_error', value='')
586

  
587
    Combo.objects.create(base_url='https://combo.dev.publik.love',
588
                         template_name='portal-user')
589
    Wcs.objects.create(base_url='https://wcs.dev.publik.love')
590
    Fargo.objects.create(base_url='https://fargo.dev.publik.love')
591

  
592
    auto_conf_mocked_post.cpt = 0
593
    with override_settings(MATOMO_FEED=CONFIG):
594
        assert auto_configure_matomo() is False
595
    message = Variable.objects.get(name='matomo_error').value
596
    assert message == 'get_javascript_tag fails'
tox.ini
52 52
	multitenant: systemd-python
53 53
	http://git.entrouvert.org/debian/django-tenant-schemas.git/snapshot/django-tenant-schemas-master.tar.gz
54 54
	httmock
55
	lxml
55 56
	requests
56 57
commands =
57 58
	./getlasso.sh
58
-