Infinite redirect loop bug in dataset package download
Description
Once a dataset (zbirka) is saved with an uploaded package (datoteka), it can be downladed through the UI using the download (Prenos) button on the dataset page. But after publishing of a draft, or any subsequent modification/save of dataset metadata, download no longer works, and attempts to access the dataset URL result in an infinite redirect loop of the download link to itself.
The source of the error is duplication of form input parameters in the template
ckanext/dgu/theme/templates/package/edit_form.html
.
<div class="hidden-resource-fields">
<!--! Because the resource fields are explicitly set in resource_columns, we need
to pull the others from the resource keys, strip the resource_columns and then
ignore resource_type, id and resource_group_id.
Code is long hand to keep the list comp understandable -->
{% for col in h.resource_extra_fields(res) %}
<input id="individual_resources__{{num}}__{{col}}" name="individual_resources__{{num}}__{{col}}" type="hidden" value="{{res.get(col, '')}}"/>
{% endfor %}
<span class="resource-url-type"><input name="individual_resources__{{num}}__url_type" type="hidden" value="{{res.get('url_type', '')}}" /></span>
<span class="resource-id"><input name="individual_resources__{{num}}__id" type="hidden" value="{{res.get('id', '')}}" autocomplete="off" /></span>
<span class="resource-type"><input name="individual_resources__{{num}}__resource_type" type="hidden" value="{{res.get('resource_type', '')}}" /></span>
</div>
With changes in commit 0135543e, the form field individual_resources__{{num}}__url_type
gets specified twice, once in the loop over resource_extra_fields
, and once explicitly in the resource-url-type
span.
This causes the POST parameter value of individual_resources__{{num}}__url_type
to become a serialized list of form ['upload', 'upload']
.
With each subsequent modification of dataset metadata, the value of the POST parameter continues to double itself as [['upload', 'upload'], ['upload', 'upload']]
, etc.
Notice: The same implementation applies to timeseries_resources
and additional_resources
fields, so most likely these are also affected.
Package download requests are handled by the opsi_resource_download
method of the PackageController
class in ckanext/dgu/controllers/package.py
.
def opsi_resource_download(self, id, resource_id, filename=None):
# ...
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'auth_user_obj': c.userobj}
try:
rsc = get_action('resource_show')(context, {'id': resource_id})
pkg = get_action('package_show')(context, {'id': id})
# ...
if rsc.get('url_type') == 'upload':
# ...
elif not 'url' in rsc:
abort(404, _('No download is available'))
redirect(rsc['url'])
With the condition rsc.get('url_type') == 'upload'
failing, execution jumps directly to the last line, invariably inducing a redirect.
Proof of concept workaround
Package downloads function properly after arbitrarily many dataset metadata modification / saves, if the resource-url-type
span is commented out in edit_form.html
:
<!-- <span class="resource-url-type"><input name="individual_resources__{{num}}__url_type" type="hidden" value="{{res.get('url_type', '')}}" /></span> -->
The implications of such a change remain to be thought through.