Repository: OCHA-DAP/hdx-ckan

Issue:
Test for unintended permissions via API
Have we left any security holes via the API?
- A non-sysadmin should not be able to add/edit/delete a group (country)
- A non-sysadmin should not be able to add/edit/delete an org
- A non-sysadmin should not be able to see user emails (other than their own) (note: Luis is pulling a list of user emails via the API, presumably with his sysadmin account)
- A NEAO should not be able to add a dataset for the org
- A NEAO should not be able to become a member of an org
- No one should be able to become a member of a group (country)



Discussion:
This can be done with nosetests using the function call_action_api() from import ckan.tests

this should be done after we have in place the final flow


Resolving patch:
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/auth.py b/ckanext-hdx_theme/ckanext/hdx_theme/auth.py
--- a/ckanext-hdx_theme/ckanext/hdx_theme/auth.py
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/auth.py
@@ -1,11 +1,18 @@
 import ckan.new_authz as new_authz
+import ckan.logic as logic
 
 from ckan.lib.base import _
 
+
 def hdx_basic_user_info(context, data_dict):
     logged_in = new_authz.auth_is_loggedin_user()
     if logged_in:
         return {'success': True}
     else:
         return {'success': False,
-                'msg': _("You must be logged in to access basic organization member info.")}
\ No newline at end of file
+                'msg': _("You must be logged in to access basic organization member info.")}
+
+
+@logic.auth_sysadmins_check
+def group_member_create(context, data_dict):
+    return {'success': False, 'msg': _('Nobody can add a member to a country in HDX')}
\ No newline at end of file
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/plugin.py b/ckanext-hdx_theme/ckanext/hdx_theme/plugin.py
--- a/ckanext-hdx_theme/ckanext/hdx_theme/plugin.py
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/plugin.py
@@ -111,7 +111,8 @@ def get_actions(self):
         }
     def get_auth_functions(self):
         return {
-                'hdx_basic_user_info': auth.hdx_basic_user_info
+                'hdx_basic_user_info': auth.hdx_basic_user_info,
+                'group_member_create': auth.group_member_create
                 }
     
     def make_middleware(self, app, config):


Test patch:
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/tests/hdx_test_base.py b/ckanext-hdx_theme/ckanext/hdx_theme/tests/hdx_test_base.py
--- a/ckanext-hdx_theme/ckanext/hdx_theme/tests/hdx_test_base.py
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/tests/hdx_test_base.py
@@ -22,7 +22,7 @@ def _get_test_app():
     return app
 
 
-def _load_plugin(plugin):
+def load_plugin(plugin):
     plugins = set(config['ckan.plugins'].strip().split())
     plugins.add(plugin.strip())
     config['ckan.plugins'] = ' '.join(plugins)
@@ -36,7 +36,7 @@ def _create_test_data(cls):
 
     @classmethod
     def _load_plugins(cls):
-        _load_plugin('hdx_theme')
+        load_plugin('hdx_theme')
 
     @classmethod
     def setup_class(cls):
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_dataset.py b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_dataset.py
new file mode 100644
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_dataset.py
@@ -0,0 +1,54 @@
+'''
+Created on Jul 25, 2014
+
+@author: alexandru-m-g
+'''
+
+import logging as logging
+import ckan.model as model
+import ckan.tests as tests
+
+import ckanext.hdx_theme.tests.hdx_test_base as hdx_test_base
+
+log = logging.getLogger(__name__)
+
+
+class TestDatasetAuth(hdx_test_base.HdxBaseTest):
+    @classmethod
+    def _load_plugins(cls):
+        hdx_test_base.load_plugin('dataset_auth hdx_theme')
+
+    def test_create_dataset_with_org(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+
+        group_result = tests.call_action_api(self.app, 'group_create',
+                                             name='test_group_e', title='Test Group E',
+                                             apikey=testsysadmin.apikey, status=200)
+        org_result = \
+            tests.call_action_api(self.app, 'organization_create', name='test_org_e',
+                                  title='Test Org E',
+                                  apikey=testsysadmin.apikey, status=200)
+
+        result = \
+            tests.call_action_api(self.app, 'package_create', name='test-dataset',
+                                  private=False, package_creator='test-creator',
+                                  dataset_source='Test Source', license_id='cc-by-igo',
+                                  title='Test Dataset', notes='some_description',
+                                  groups=[{'id':group_result['id']}], owner_org=org_result['id'],
+                                  apikey=user.apikey, status=403)
+        assert True, "tester is not part of Test Org E so he can't create a dataset for it"
+
+        tests.call_action_api(self.app, 'organization_member_create',
+                              id=org_result['id'], username='tester', role='editor',
+                              apikey=testsysadmin.apikey, status=200)
+
+        result = \
+            tests.call_action_api(self.app, 'package_create', name='test-dataset',
+                                  private=False, package_creator='test-creator',
+                                  dataset_source='Test Source', license_id='cc-by-igo',
+                                  title='Test Dataset', notes='some_description',
+                                  groups=[{'id':group_result['id']}], owner_org=org_result['id'],
+                                  apikey=user.apikey, status=200)
+            
+        assert True, "tester is now editor of Test Org E so he can create a dataset for it"
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_groups.py b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_groups.py
new file mode 100644
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_groups.py
@@ -0,0 +1,54 @@
+'''
+Created on Jul 24, 2014
+
+@author: alexandru-m-g
+'''
+
+import logging as logging
+import ckan.model as model
+import ckan.tests as tests
+
+import ckanext.hdx_theme.tests.hdx_test_base as hdx_test_base
+
+log = logging.getLogger(__name__)
+
+
+class TestGroupAuth(hdx_test_base.HdxBaseTest):
+
+    def test_create_country(self):
+        user = model.User.by_name('tester')
+        tests.call_action_api(self.app, 'group_create', name='test_group_a',
+                              title='Test Group A',
+                              apikey=user.apikey, status=403)
+        assert True
+
+    def test_edit_country(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        create_result = tests.call_action_api(self.app, 'group_create',
+                                              name='test_group_b', title='Test Group B',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'group_update', id=create_result['id'],
+                                       title='Test Group B CHANGED',
+                                       apikey=user.apikey, status=403)
+        assert True, 'user should not be allowed to modify the group'
+
+    def test_delete_country(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        create_result = tests.call_action_api(self.app, 'group_create',
+                                              name='test_group_c', title='Test Group C',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'group_delete', id=create_result['id'],
+                                       apikey=user.apikey, status=403)
+        assert True, 'user should not be allowed to delete the group'
+
+    def test_create_country_member(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        create_result = tests.call_action_api(self.app, 'group_create',
+                                              name='test_group_d', title='Test Group D',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'group_member_create',
+                              id=create_result['id'], username='tester', role='editor',
+                              apikey=testsysadmin.apikey, status=403)
+        assert True, 'Country members shouldn\'t be allowed'
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_orgs.py b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_orgs.py
new file mode 100644
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_orgs.py
@@ -0,0 +1,61 @@
+'''
+Created on Jul 24, 2014
+
+@author: alexandru-m-g
+'''
+
+import logging as logging
+import ckan.model as model
+import ckan.tests as tests
+
+import ckanext.hdx_theme.tests.hdx_test_base as hdx_test_base
+
+log = logging.getLogger(__name__)
+
+
+class TestOrgAuth(hdx_test_base.HdxBaseTest):
+
+    def test_create_org(self):
+        user = model.User.by_name('tester')
+        tests.call_action_api(self.app, 'organization_create', name='test_org_a',
+                              title='Test Org A',
+                              apikey=user.apikey, status=403)
+        testsysadmin = model.User.by_name('testsysadmin')
+        tests.call_action_api(self.app, 'organization_create', name='test_org_a_admin',
+                              title='Test Org A Admin',
+                              apikey=testsysadmin.apikey, status=200)
+        assert True
+
+    def test_edit_org(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        create_result = tests.call_action_api(self.app, 'organization_create',
+                                              name='test_org_b', title='Test Org B',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'organization_update', id=create_result['id'],
+                              title='Test Org B CHANGED',
+                              apikey=user.apikey, status=403)
+        assert True, 'user should not be allowed to modify the org'
+
+    def test_delete_org(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        create_result = tests.call_action_api(self.app, 'organization_create',
+                                              name='test_org_c', title='Test Org C',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'organization_delete', id=create_result['id'],
+                                       apikey=user.apikey, status=403)
+        assert True, 'user should not be allowed to delete the org'
+
+    def test_create_org_member(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        create_result = tests.call_action_api(self.app, 'organization_create',
+                                              name='test_org_d', title='Test Org D',
+                                              apikey=testsysadmin.apikey, status=200)
+        tests.call_action_api(self.app, 'organization_member_create',
+                              id=create_result['id'], username='tester', role='editor',
+                              apikey=user.apikey, status=403)
+        assert True, 'user shoudn\'t be allowed to add himself as a member'
+
+
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_user.py b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_user.py
new file mode 100644
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/tests/test_auth/test_user.py
@@ -0,0 +1,30 @@
+'''
+Created on Jul 25, 2014
+
+@author: alexandru-m-g
+'''
+import logging as logging
+import ckan.model as model
+import ckan.tests as tests
+
+import ckanext.hdx_theme.tests.hdx_test_base as hdx_test_base
+
+log = logging.getLogger(__name__)
+
+
+class TestUserAuth(hdx_test_base.HdxBaseTest):
+    def test_access_to_user_emails(self):
+        testsysadmin = model.User.by_name('testsysadmin')
+        user = model.User.by_name('tester')
+        user2 = model.User.by_name('annafan')
+        user.email = 'test@testemail.com'
+
+        users = tests.call_action_api(self.app, 'user_list', q='tester',
+                                      apikey=testsysadmin.apikey, status=200)
+        assert len(users) == 1
+        assert users[0].get('email', None), "testsysadmin shoud be able to see user's email"
+
+        users = tests.call_action_api(self.app, 'user_list', q='tester',
+                                      apikey=user2.apikey, status=200)
+        assert len(users) == 1
+        assert not users[0].get('email', None), "user2 shoudn't be able to see user's email"