diff --git a/ckan/logic/__init__.py b/ckan/logic/__init__.py index 4a474e26432..209d79f4b77 100644 --- a/ckan/logic/__init__.py +++ b/ckan/logic/__init__.py @@ -502,6 +502,19 @@ def get_or_bust(data_dict, keys): return values[0] return tuple(values) +def validate(schema_func): + ''' A decorator that validates an action function against a given schema + ''' + def action_decorator(action): + @functools.wraps(action) + def wrapper(context, data_dict, **kwargs): + schema = schema_func() + data_dict, errors = _validate(data_dict, schema, context) + if errors: + raise ValidationError(errors) + return action(context, data_dict) + return wrapper + return action_decorator def side_effect_free(action): '''A decorator that marks the given action function as side-effect-free. diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index ca46ac2ff57..6c95563f0b1 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -2046,6 +2046,7 @@ def vocabulary_show(context, data_dict): vocabulary_dict = model_dictize.vocabulary_dictize(vocabulary, context) return vocabulary_dict +@logic.validate(logic.schema.default_activity_list_schema) def user_activity_list(context, data_dict): '''Return a user's public activity stream. @@ -2071,12 +2072,12 @@ def user_activity_list(context, data_dict): model = context['model'] - user_ref = _get_or_bust(data_dict, 'id') # May be user name or id. + user_ref = data_dict.get('id') # May be user name or id. user = model.User.get(user_ref) if user is None: raise logic.NotFound - offset = int(data_dict.get('offset', 0)) + offset = data_dict.get('offset', 0) limit = int( data_dict.get('limit', config.get('ckan.activity_list_limit', 31))) diff --git a/ckan/logic/schema.py b/ckan/logic/schema.py index 9425b337245..06c395d1109 100644 --- a/ckan/logic/schema.py +++ b/ckan/logic/schema.py @@ -540,6 +540,12 @@ def default_dashboard_activity_list_schema(): return schema +def default_activity_list_schema(): + schema = default_pagination_schema() + schema['id'] = [not_missing, unicode] + return schema + + def default_autocomplete_schema(): schema = { 'q': [not_missing, unicode], diff --git a/ckan/new_tests/logic/action/test_get.py b/ckan/new_tests/logic/action/test_get.py new file mode 100644 index 00000000000..1d0aae3b9bf --- /dev/null +++ b/ckan/new_tests/logic/action/test_get.py @@ -0,0 +1,22 @@ +import nose.tools +import nose.case +import ckan.logic as logic +import ckan.new_tests.helpers as helpers +import ckan.new_tests.factories as factories + +class TestBadLimitQueryParameters(object): + '''test class for #1258 non-int query parameters cause 500 errors''' + @classmethod + def setup_class(cls): + helpers.reset_db() + + def teardown(self): + import ckan.model as model + model.repo.rebuild_db() + + def test_user_activity_list(self): + user = factories.User() + nose.tools.assert_raises(logic.ValidationError, helpers.call_action, + 'user_activity_list', id=user['name'], limit='not_an_int', + offset='not_an_int' + )