api: add possibility of http basic auth access to the ics endpoint (#16792)
This commit is contained in:
parent
412844fbdd
commit
fcfda2d576
|
@ -1445,7 +1445,8 @@ def test_api_geojson_formdata(pub, local_user):
|
|||
formdef.store()
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson', user=local_user), status=404)
|
||||
|
||||
def test_api_ics_formdata(pub, local_user):
|
||||
@pytest.fixture
|
||||
def ics_data(local_user):
|
||||
Role.wipe()
|
||||
role = Role(name='test')
|
||||
role.store()
|
||||
|
@ -1462,11 +1463,6 @@ def test_api_ics_formdata(pub, local_user):
|
|||
data_class = formdef.data_class()
|
||||
data_class.wipe()
|
||||
|
||||
# check access is denied if the user has not the appropriate role
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user), status=403)
|
||||
# even if there's an anonymse parameter
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?anonymise', user=local_user), status=403)
|
||||
|
||||
date = datetime.datetime(2014, 1, 20, 12, 00)
|
||||
for i in range(30):
|
||||
formdata = data_class()
|
||||
|
@ -1479,6 +1475,14 @@ def test_api_ics_formdata(pub, local_user):
|
|||
formdata.jump_status('finished')
|
||||
formdata.store()
|
||||
|
||||
def test_api_ics_formdata(pub, local_user, ics_data):
|
||||
role = Role.select()[0]
|
||||
|
||||
# check access is denied if the user has not the appropriate role
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar', user=local_user), status=403)
|
||||
# even if there's an anonymse parameter
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/foobar?anonymise', user=local_user), status=403)
|
||||
|
||||
# add proper role to user
|
||||
local_user.roles = [role.id]
|
||||
local_user.store()
|
||||
|
@ -1495,6 +1499,36 @@ def test_api_ics_formdata(pub, local_user):
|
|||
# check 404 on erroneous field var
|
||||
resp = get_app(pub).get(sign_uri('/api/forms/test/ics/xxx', user=local_user), status=404)
|
||||
|
||||
def test_api_ics_formdata_http_auth(pub, local_user, ics_data):
|
||||
role = Role.select()[0]
|
||||
|
||||
# no access
|
||||
app = get_app(pub)
|
||||
app.authorization = ('Basic', ('user', 'password'))
|
||||
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403)
|
||||
|
||||
# add authentication info
|
||||
pub.load_site_options()
|
||||
pub.site_options.add_section('api-http-auth-ics')
|
||||
pub.site_options.set('api-http-auth-ics', 'user', 'password')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
|
||||
# check access is denied if the user has not the appropriate role
|
||||
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403)
|
||||
|
||||
# add proper role to user
|
||||
local_user.roles = [role.id]
|
||||
local_user.store()
|
||||
|
||||
# check it gets the data
|
||||
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=200)
|
||||
assert resp.headers['content-type'] == 'text/calendar; charset=utf-8'
|
||||
assert resp.body.count('BEGIN:VEVENT') == 10
|
||||
|
||||
# check it fails with a different password
|
||||
app.authorization = ('Basic', ('user', 'password2'))
|
||||
resp = app.get('/api/forms/test/ics/foobar?email=%s' % local_user.email, status=403)
|
||||
|
||||
def test_roles(pub, local_user):
|
||||
Role.wipe()
|
||||
role = Role(name='Hello World')
|
||||
|
|
14
wcs/api.py
14
wcs/api.py
|
@ -117,16 +117,13 @@ class ApiFormPage(BackofficeFormPage):
|
|||
self.formdef = FormDef.get_by_urlname(component)
|
||||
except KeyError:
|
||||
raise TraversalError()
|
||||
# check access for all paths, to block access to formdata that would
|
||||
# otherwise be accessible if the user is the submitter.
|
||||
self.check_access()
|
||||
|
||||
def check_access(self):
|
||||
def check_access(self, api_name=None):
|
||||
if 'anonymise' in get_request().form:
|
||||
if not is_url_signed() or (get_request().user and get_request().user.is_admin):
|
||||
raise AccessForbiddenError('user not authenticated')
|
||||
else:
|
||||
api_user = get_user_from_api_query_string()
|
||||
api_user = get_user_from_api_query_string(api_name=api_name)
|
||||
if not api_user:
|
||||
if get_request().user and get_request().user.is_admin:
|
||||
return # grant access to admins, to ease debug
|
||||
|
@ -138,6 +135,9 @@ class ApiFormPage(BackofficeFormPage):
|
|||
if component == 'ics':
|
||||
return self.ics()
|
||||
|
||||
# check access for all paths, to block access to formdata that would
|
||||
# otherwise be accessible if the user is the submitter.
|
||||
self.check_access()
|
||||
try:
|
||||
formdata = self.formdef.data_class().get(component)
|
||||
except KeyError:
|
||||
|
@ -147,10 +147,6 @@ class ApiFormPage(BackofficeFormPage):
|
|||
|
||||
class ApiFormsDirectory(Directory):
|
||||
def _q_lookup(self, component):
|
||||
if not is_url_signed():
|
||||
# grant access to admins, to ease debug
|
||||
if not (get_request().user and get_request().user.is_admin):
|
||||
raise AccessForbiddenError('user not authenticated')
|
||||
return ApiFormPage(component)
|
||||
|
||||
|
||||
|
|
|
@ -98,10 +98,22 @@ def is_url_signed(utcnow=None, duration=DEFAULT_DURATION):
|
|||
return True
|
||||
|
||||
|
||||
def get_user_from_api_query_string():
|
||||
if not is_url_signed():
|
||||
def get_user_from_api_query_string(api_name=None):
|
||||
auth_header = get_request().get_header('Authorization', '')
|
||||
if auth_header and api_name:
|
||||
if not auth_header.startswith('Basic '):
|
||||
# we do not handle other authentication schemes
|
||||
raise AccessForbiddenError('unhandled authorization header')
|
||||
auth_header = auth_header.split(' ', 1)[1]
|
||||
username, password = base64.decodestring(auth_header).split(':', 1)
|
||||
configured_password = get_publisher().get_site_option(
|
||||
username, section='api-http-auth-%s' % api_name)
|
||||
if configured_password != password:
|
||||
raise AccessForbiddenError('invalid authorization')
|
||||
elif not is_url_signed():
|
||||
return None
|
||||
# Signature is good. Now looking for the user, by email/NameID.
|
||||
# Signature or auth header are ok.
|
||||
# Look for the user, by email/NameID.
|
||||
user = None
|
||||
if get_request().form.get('email'):
|
||||
email = get_request().form.get('email')
|
||||
|
|
|
@ -1604,7 +1604,8 @@ class FormPage(Directory):
|
|||
if 'anonymise' in get_request().form:
|
||||
# api/ will let this pass but we don't want that.
|
||||
raise errors.AccessForbiddenError()
|
||||
self.check_access()
|
||||
self.check_access('ics')
|
||||
user = get_user_from_api_query_string('ics') or get_request().user
|
||||
|
||||
formdef = self.formdef
|
||||
selected_filter = self.get_filter_from_query()
|
||||
|
@ -1625,8 +1626,6 @@ class FormPage(Directory):
|
|||
else:
|
||||
raise errors.TraversalError()
|
||||
|
||||
user = get_user_from_api_query_string() or get_request().user
|
||||
|
||||
formdatas, total_count = FormDefUI(formdef).get_listing_items(
|
||||
selected_filter, user=user, query=query, criterias=criterias)
|
||||
|
||||
|
|
Loading…
Reference in New Issue