0001-settings-add-an-xml-storage-for-site-options-48751.patch
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 |
- |