Projet

Général

Profil

0001-settings-add-an-xml-storage-for-site-options-48751.patch

Nicolas Roche, 25 novembre 2020 10:53

Télécharger (9,3 ko)

Voir les différences:

Subject: [PATCH 1/2] settings: add an xml storage for site options (#48751)

 tests/test_api.py       | 46 +++++++++++++++++++++++++++++++
 wcs/parameters.py       | 61 +++++++++++++++++++++++++++++++++++++++++
 wcs/qommon/publisher.py | 28 +++++++++++++++++--
 3 files changed, 133 insertions(+), 2 deletions(-)
 create mode 100644 wcs/parameters.py
tests/test_api.py
293 293
            '1234'
294 294
            )
295 295
    url = signed_url[len('http://example.net'):]
296 296
    output = get_app(pub).get(url)
297 297
    assert output.json['user_display_name'] == u'Jean Darmette'
298 298
    assert [x['name'] for x in output.json['user_roles']] == ['Foo bar']
299 299
    assert [x['slug'] for x in output.json['user_roles']] == ['foo-bar']
300 300

  
301
    # authentication info using XML
302
    open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('')
303
    signed_url = sign_url(
304
            'http://example.net/api/user/?format=json&orig=coucou&email=%s' % urllib.quote(local_user.email),
305
            '1234'
306
            )
307
    url = signed_url[len('http://example.net'):]
308
    output = get_app(pub).get(url, status=403)
309
    assert output.json['err_desc'] == 'invalid orig'
310
    parameters = """<?xml version="1.0"?>
311
<parameters>
312
  <api>
313
    <secrets>
314
       <coucou>1234</coucou>
315
    </secrets>
316
  </api>
317
</parameters>
318
"""
319
    with open(os.path.join(pub.app_dir, 'site-parameters.xml'), 'w') as fd:
320
        fd.write(parameters)
321
    url = signed_url[len('http://example.net'):]
322
    output = get_app(pub).get(url)
323
    assert output.json['user_display_name'] == u'Jean Darmette'
324

  
301 325

  
302 326
def test_is_url_signed_check_nonce(pub, local_user, freezer):
303 327
    ORIG = 'xxx'
304 328
    KEY = 'xxx'
305 329

  
306 330
    pub.site_options.add_section('api-secrets')
307 331
    pub.site_options.set('api-secrets', ORIG, KEY)
308 332
    pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
......
2776 2800
    resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=200)
2777 2801
    assert resp.headers['content-type'] == 'text/calendar; charset=utf-8'
2778 2802
    assert resp.text.count('BEGIN:VEVENT') == 10
2779 2803

  
2780 2804
    # check it fails with a different password
2781 2805
    app.authorization = ('Basic', ('user', 'password2'))
2782 2806
    resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=401)
2783 2807

  
2808
    # add authentication info using XML
2809
    parameters = """<?xml version="1.0"?>
2810
<parameters>
2811
  <api>
2812
    <http>
2813
      <auth>
2814
        <ics>
2815
          <user>password2</user>
2816
        </ics>
2817
      </auth>
2818
    </http>
2819
  </api>
2820
</parameters>
2821
"""
2822
    with open(os.path.join(pub.app_dir, 'site-parameters.xml'), 'w') as fd:
2823
        fd.write(parameters)
2824

  
2825
    # check it gets the data
2826
    resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=200)
2827
    assert resp.headers['content-type'] == 'text/calendar; charset=utf-8'
2828
    assert resp.text.count('BEGIN:VEVENT') == 10
2829

  
2784 2830

  
2785 2831
def test_api_ics_formdata_custom_view(pub, local_user, ics_data):
2786 2832
    role = Role.select()[0]
2787 2833

  
2788 2834
    formdef = FormDef.get_by_urlname('test')
2789 2835

  
2790 2836
    pub.custom_view_class.wipe()
2791 2837
    custom_view = pub.custom_view_class()
wcs/parameters.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2020  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 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 General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
from collections import OrderedDict
18
import xml.etree.ElementTree as ET
19

  
20
from quixote import get_publisher
21

  
22
from wcs.qommon.xml_storage import XmlStorableObject
23

  
24

  
25
class Parameters(XmlStorableObject):
26

  
27
    def __init__(self):
28
        self.parameters = OrderedDict([('options', OrderedDict())])
29
        self.id = None
30

  
31
    @classmethod
32
    def import_from_xml_tree(cls, tree, include_id=False, charset=None, snapshot=False):
33
        if charset is None:
34
            charset = get_publisher().site_charset
35
        obj = cls()
36

  
37
        # if the tree we get is actually a ElementTree for real, we get its
38
        # root element and go on happily.
39
        if not ET.iselement(tree):
40
            tree = tree.getroot()
41

  
42
        # parse XML with uniq tags and without attributes
43
        def etree_to_dict(element):
44
            d = OrderedDict()
45
            for child in element.getchildren():
46
                d[child.tag.lower()] = etree_to_dict(child)
47
            if not d:
48
                return element.text
49
            return d
50

  
51
        obj.parameters = etree_to_dict(tree) or OrderedDict()
52
        return obj
53

  
54
    def get(self, option, section='options'):
55
        d = self.parameters
56
        for tag in section.split('-'):
57
            d = d.get(tag)
58
            if not d:
59
                return None
60
        value = d.get(option)
61
        return value
wcs/qommon/publisher.py
34 34
import random
35 35
import re
36 36
import socket
37 37
import sys
38 38
import time
39 39
import traceback
40 40

  
41 41
import linecache
42
import xml.etree.ElementTree as ET
43 42

  
44 43
from django.conf import settings
45 44
from django.http import Http404
46 45
from django.utils import six
47 46
from django.utils import translation
48 47
from django.utils.encoding import force_text, force_bytes
49 48
from django.utils.six import StringIO
50 49

  
......
86 85
    unpickler_class = None
87 86

  
88 87
    after_login_url = ''
89 88
    qommon_static_dir = 'static/'
90 89
    qommon_admin_css = 'css/dc2/admin.css'
91 90
    default_theme = 'default'
92 91

  
93 92
    site_options = None
93
    site_parameters = None
94 94
    site_charset = 'utf-8'
95 95
    missing_appdir_redirect = None
96 96
    use_sms_feature = True
97 97

  
98 98
    gettext = lambda self, message: message
99 99
    ngettext = lambda self, msgid1, msgid2, n: msgid1
100 100

  
101 101
    app_dir = None
......
373 373
            self.load_site_options()
374 374
        try:
375 375
            return (self.site_options.get('options', option) == 'true')
376 376
        except ConfigParser.NoSectionError:
377 377
            return defaults.get(option, False)
378 378
        except ConfigParser.NoOptionError:
379 379
            return defaults.get(option, False)
380 380

  
381
    def get_site_option(self, option, section='options'):
381
    def _get_site_option(self, option, section='options'):
382 382
        defaults = {
383 383
            'options': {
384 384
                'unused-files-behaviour': 'remove',
385 385
                'relatable-hosts': '',
386 386
            },
387 387
        }
388 388
        if self.site_options is None:
389 389
            self.load_site_options()
......
400 400
        storages = {}
401 401
        for section, definition in self.site_options.items():
402 402
            if section.startswith('storage-') and 'label' in definition and 'class' in definition:
403 403
                storage_id = section[8:]
404 404
                storages[storage_id] = dict(definition)
405 405
                storages[storage_id]['id'] = storage_id
406 406
        return storages
407 407

  
408
    def load_site_parameters(self):
409
        from wcs.parameters import Parameters
410

  
411
        if not self.site_parameters:
412
            self.site_parameters = Parameters()
413
        filename = os.path.join(self.app_dir, 'site-parameters.xml')
414
        if not os.path.exists(filename):
415
            return
416
        fd = open(filename, 'rb')
417
        try:
418
            self.site_parameters = self.site_parameters.storage_load(fd)
419
        except:
420
            self.get_app_logger().error('failed to read site options XML file')
421
            return
422

  
423
    def get_site_parameter(self, option, section='options'):
424
        if self.site_parameters is None:
425
            self.load_site_parameters()
426
        return self.site_parameters.get(option, section)
427

  
428
    def get_site_option(self, option, section='options'):
429
        return self.get_site_parameter(option, section) or self._get_site_option(option, section)
430

  
408 431
    def set_config(self, request = None):
409 432
        self.reload_cfg()
410 433
        self.site_options = None # reset at the beginning of a request
434
        self.site_parameters = None
411 435
        debug_cfg = self.cfg.get('debug', {})
412 436
        self.logger.error_email = debug_cfg.get('error_email')
413 437
        self.config.display_exceptions = debug_cfg.get('display_exceptions')
414 438
        self.config.form_tokens = True
415 439
        self.config.session_cookie_httponly = True
416 440
        self.config.allowed_methods = ['GET', 'HEAD', 'POST', 'PUT']
417 441

  
418 442
        if request:
419
-