Skip to content

Commit

Permalink
Merge pull request #1226 from okfn/1226-user-create-option
Browse files Browse the repository at this point in the history
Disable user registration with a config
  • Loading branch information
johnglover committed Nov 5, 2013
2 parents be38950 + ff7e38e commit cf55b02
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 22 deletions.
1 change: 1 addition & 0 deletions ckan/config/deployment.ini_tmpl
Expand Up @@ -66,6 +66,7 @@ ckan.auth.user_create_organizations = true
ckan.auth.user_delete_groups = true ckan.auth.user_delete_groups = true
ckan.auth.user_delete_organizations = true ckan.auth.user_delete_organizations = true
ckan.auth.create_user_via_api = false ckan.auth.create_user_via_api = false
ckan.auth.create_user_via_web = true




## Search Settings ## Search Settings
Expand Down
21 changes: 14 additions & 7 deletions ckan/logic/auth/create.py
Expand Up @@ -103,15 +103,22 @@ def rating_create(context, data_dict):
# No authz check in the logic function # No authz check in the logic function
return {'success': True} return {'success': True}



@logic.auth_allow_anonymous_access @logic.auth_allow_anonymous_access
def user_create(context, data_dict=None): def user_create(context, data_dict=None):
user = context['user'] using_api = 'api_version' in context

create_user_via_api = new_authz.check_config_permission(
if ('api_version' in context 'create_user_via_api')
and not new_authz.check_config_permission('create_user_via_api')): create_user_via_web = new_authz.check_config_permission(
return {'success': False, 'msg': _('User %s not authorized to create users') % user} 'create_user_via_web')
else:
return {'success': True} if using_api and not create_user_via_api:
return {'success': False, 'msg': _('User {user} not authorized to '
'create users via the API').format(user=context.get('user'))}
if not using_api and not create_user_via_web:
return {'success': False, 'msg': _('Not authorized to '
'create users')}
return {'success': True}


def user_invite(context, data_dict=None): def user_invite(context, data_dict=None):
context['id'] = context.get('group_id') context['id'] = context.get('group_id')
Expand Down
2 changes: 2 additions & 0 deletions ckan/new_authz.py
Expand Up @@ -96,6 +96,7 @@ def _build(self):


def clear_auth_functions_cache(): def clear_auth_functions_cache():
_AuthFunctions.clear() _AuthFunctions.clear()
CONFIG_PERMISSIONS.clear()




def auth_functions_list(): def auth_functions_list():
Expand Down Expand Up @@ -350,6 +351,7 @@ def get_user_id_for_username(user_name, allow_none=False):
'user_delete_groups': True, 'user_delete_groups': True,
'user_delete_organizations': True, 'user_delete_organizations': True,
'create_user_via_api': False, 'create_user_via_api': False,
'create_user_via_web': True,
} }


CONFIG_PERMISSIONS = {} CONFIG_PERMISSIONS = {}
Expand Down
4 changes: 3 additions & 1 deletion ckan/templates/header.html
Expand Up @@ -50,7 +50,9 @@
<ul class="unstyled"> <ul class="unstyled">
{% block header_account_notlogged %} {% block header_account_notlogged %}
<li>{% link_for _('Log in'), controller='user', action='login' %}</li> <li>{% link_for _('Log in'), controller='user', action='login' %}</li>
<li>{% link_for _('Register'), controller='user', action='register', class_='sub' %}</li> {% if h.check_access('user_create') %}
<li>{% link_for _('Register'), controller='user', action='register', class_='sub' %}</li>
{% endif %}
{% endblock %} {% endblock %}
</ul> </ul>
</nav> </nav>
Expand Down
20 changes: 11 additions & 9 deletions ckan/templates/user/login.html
Expand Up @@ -18,15 +18,17 @@ <h1 class="page-heading">{% block page_heading %}{{ _('Login') }}{% endblock %}<
{% endblock %} {% endblock %}


{% block secondary_content %} {% block secondary_content %}
<section class="module module-narrow module-shallow"> {% if h.check_access('user_create') %}
<h2 class="module-heading">{{ _('Need an Account?') }}</h2> <section class="module module-narrow module-shallow">
<div class="module-content"> <h2 class="module-heading">{{ _('Need an Account?') }}</h2>
<p>{% trans %}Then sign right up, it only takes a minute.{% endtrans %}</p> <div class="module-content">
<p class="action"> <p>{% trans %}Then sign right up, it only takes a minute.{% endtrans %}</p>
<a class="btn" href="{{ h.url_for(controller='user', action='register') }}">{{ _('Create an Account') }}</a> <p class="action">
</p> <a class="btn" href="{{ h.url_for(controller='user', action='register') }}">{{ _('Create an Account') }}</a>
</div> </p>
</section> </div>
</section>
{% endif %}


<section class="module module-narrow module-shallow"> <section class="module module-narrow module-shallow">
<h2 class="module-heading">{{ _('Forgotten your password?') }}</h2> <h2 class="module-heading">{{ _('Forgotten your password?') }}</h2>
Expand Down
191 changes: 188 additions & 3 deletions ckan/tests/functional/api/test_user.py
@@ -1,20 +1,27 @@
import paste
from pylons import config
from nose.tools import assert_equal from nose.tools import assert_equal


import ckan.logic as logic import ckan.logic as logic
import ckan.new_authz as new_authz
from ckan import model from ckan import model
from ckan.lib.create_test_data import CreateTestData from ckan.lib.create_test_data import CreateTestData
from ckan.tests import TestController as ControllerTestCase from ckan.tests import TestController as ControllerTestCase
from ckan.tests.pylons_controller import PylonsTestCase
from ckan.tests import url_for from ckan.tests import url_for
import ckan.config.middleware
from ckan.common import json



class TestUserApi(ControllerTestCase): class TestUserApi(ControllerTestCase):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
CreateTestData.create() CreateTestData.create()

@classmethod @classmethod
def teardown_class(cls): def teardown_class(cls):
model.repo.rebuild_db() model.repo.rebuild_db()

def test_autocomplete(self): def test_autocomplete(self):
response = self.app.get( response = self.app.get(
url=url_for(controller='api', action='user_autocomplete', ver=2), url=url_for(controller='api', action='user_autocomplete', ver=2),
Expand Down Expand Up @@ -51,8 +58,186 @@ def test_autocomplete_limit(self):
print response.json print response.json
assert_equal(len(response.json), 1) assert_equal(len(response.json), 1)



class TestCreateUserApiDisabled(PylonsTestCase):
'''
Tests for the creating user when create_user_via_api is disabled.
'''

@classmethod
def setup_class(cls):
CreateTestData.create()
cls._original_config = config.copy()
new_authz.clear_auth_functions_cache()
wsgiapp = ckan.config.middleware.make_app(
config['global_conf'], **config)
cls.app = paste.fixture.TestApp(wsgiapp)
cls.sysadmin_user = model.User.get('testsysadmin')
PylonsTestCase.setup_class()

@classmethod
def teardown_class(cls):
config.clear()
config.update(cls._original_config)
new_authz.clear_auth_functions_cache()
PylonsTestCase.teardown_class()

model.repo.rebuild_db()

def test_user_create_api_enabled_sysadmin(self):
params = {
'name': 'testinganewusersysadmin',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post(
'/api/3/action/user_create',
json.dumps(params),
extra_environ={'Authorization': str(self.sysadmin_user.apikey)},
expect_errors=True)
res_dict = res.json
assert res_dict['success'] is True

def test_user_create_api_disabled_anon(self):
params = {
'name': 'testinganewuseranon',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post('/api/3/action/user_create', json.dumps(params),
expect_errors=True)
res_dict = res.json
assert res_dict['success'] is False


class TestCreateUserApiEnabled(PylonsTestCase):
'''
Tests for the creating user when create_user_via_api is enabled.
'''

@classmethod
def setup_class(cls):
CreateTestData.create()
cls._original_config = config.copy()
config['ckan.auth.create_user_via_api'] = True
new_authz.clear_auth_functions_cache()
wsgiapp = ckan.config.middleware.make_app(
config['global_conf'], **config)
cls.app = paste.fixture.TestApp(wsgiapp)
PylonsTestCase.setup_class()
cls.sysadmin_user = model.User.get('testsysadmin')

@classmethod
def teardown_class(cls):
config.clear()
config.update(cls._original_config)
new_authz.clear_auth_functions_cache()
PylonsTestCase.teardown_class()

model.repo.rebuild_db()

def test_user_create_api_enabled_sysadmin(self):
params = {
'name': 'testinganewusersysadmin',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post(
'/api/3/action/user_create',
json.dumps(params),
extra_environ={'Authorization': str(self.sysadmin_user.apikey)})
res_dict = res.json
assert res_dict['success'] is True

def test_user_create_api_enabled_anon(self):
params = {
'name': 'testinganewuseranon',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post('/api/3/action/user_create', json.dumps(params))
res_dict = res.json
assert res_dict['success'] is True


class TestCreateUserWebDisabled(PylonsTestCase):
'''
Tests for the creating user by create_user_via_web is disabled.
'''

@classmethod
def setup_class(cls):
CreateTestData.create()
cls._original_config = config.copy()
config['ckan.auth.create_user_via_web'] = False
new_authz.clear_auth_functions_cache()
wsgiapp = ckan.config.middleware.make_app(
config['global_conf'], **config)
cls.app = paste.fixture.TestApp(wsgiapp)
cls.sysadmin_user = model.User.get('testsysadmin')
PylonsTestCase.setup_class()

@classmethod
def teardown_class(cls):
config.clear()
config.update(cls._original_config)
new_authz.clear_auth_functions_cache()
PylonsTestCase.teardown_class()

model.repo.rebuild_db()

def test_user_create_api_disabled(self):
params = {
'name': 'testinganewuser',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post('/api/3/action/user_create', json.dumps(params),
expect_errors=True)
res_dict = res.json
assert res_dict['success'] is False


class TestCreateUserWebEnabled(PylonsTestCase):
'''
Tests for the creating user by create_user_via_web is enabled.
'''

@classmethod
def setup_class(cls):
CreateTestData.create()
cls._original_config = config.copy()
config['ckan.auth.create_user_via_web'] = True
new_authz.clear_auth_functions_cache()
wsgiapp = ckan.config.middleware.make_app(
config['global_conf'], **config)
cls.app = paste.fixture.TestApp(wsgiapp)
cls.sysadmin_user = model.User.get('testsysadmin')
PylonsTestCase.setup_class()

@classmethod
def teardown_class(cls):
config.clear()
config.update(cls._original_config)
new_authz.clear_auth_functions_cache()
PylonsTestCase.teardown_class()

model.repo.rebuild_db()

def test_user_create_api_disabled(self):
params = {
'name': 'testinganewuser',
'email': 'testinganewuser@ckan.org',
'password': 'random',
}
res = self.app.post('/api/3/action/user_create', json.dumps(params),
expect_errors=True)
res_dict = res.json
assert res_dict['success'] is False


class TestUserActions(object): class TestUserActions(object):

@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
CreateTestData.create() CreateTestData.create()
Expand Down
3 changes: 2 additions & 1 deletion ckan/tests/logic/test_auth.py
Expand Up @@ -13,8 +13,9 @@
'user_create_organizations': False, 'user_create_organizations': False,
'user_delete_groups': False, 'user_delete_groups': False,
'user_delete_organizations': False, 'user_delete_organizations': False,
'create_user_via_api': False,
'create_unowned_dataset': False, 'create_unowned_dataset': False,
'create_user_via_api': False,
'create_user_via_web': True,
} }




Expand Down
14 changes: 14 additions & 0 deletions doc/configuration.rst
Expand Up @@ -355,6 +355,20 @@ Default value: ``False``


Allow new user accounts to be created via the API. Allow new user accounts to be created via the API.


.. _ckan.auth.create_user_via_web:

ckan.auth.create_user_via_web
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Example::

ckan.auth.create_user_via_web = True

Default value: ``True``


Allow new user accounts to be created via the Web.

.. end_config-authorization .. end_config-authorization
Expand Down
3 changes: 2 additions & 1 deletion test-core.ini
Expand Up @@ -33,6 +33,7 @@ solr_url = http://127.0.0.1:8983/solr
ckan.auth.user_create_organizations = true ckan.auth.user_create_organizations = true
ckan.auth.user_create_groups = true ckan.auth.user_create_groups = true
ckan.auth.create_user_via_api = false ckan.auth.create_user_via_api = false
ckan.auth.create_user_via_web = true
ckan.auth.create_dataset_if_not_in_organization = true ckan.auth.create_dataset_if_not_in_organization = true
ckan.auth.anon_create_dataset = false ckan.auth.anon_create_dataset = false
ckan.auth.user_delete_groups=true ckan.auth.user_delete_groups=true
Expand Down Expand Up @@ -82,7 +83,7 @@ smtp.mail_from = info@test.ckan.net


ckan.locale_default = en ckan.locale_default = en
ckan.locale_order = en pt_BR ja it cs_CZ ca es fr el sv sr sr@latin no sk fi ru de pl nl bg ko_KR hu sa sl lv ckan.locale_order = en pt_BR ja it cs_CZ ca es fr el sv sr sr@latin no sk fi ru de pl nl bg ko_KR hu sa sl lv
ckan.locales_filtered_out = ckan.locales_filtered_out =


ckan.datastore.enabled = 1 ckan.datastore.enabled = 1


Expand Down

0 comments on commit cf55b02

Please sign in to comment.