Skip to content

Commit

Permalink
[#1178] Adds user to organization when inviting, and only org admins …
Browse files Browse the repository at this point in the history
…can invite
  • Loading branch information
vitorbaptista committed Aug 20, 2013
1 parent e589074 commit f2f9095
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 42 deletions.
22 changes: 17 additions & 5 deletions ckan/logic/action/create.py
Expand Up @@ -843,19 +843,25 @@ def user_create(context, data_dict):
def user_invite(context, data_dict):
'''Invite a new user.
You must be authorized to invite users.
You must be authorized to create organization members.
:param email: the email address for the new user
:param email: the email of the user to be invited to the organization
:type email: string
:param organization_id: the id or name of the organization
:type organization_id: string
:param role: role of the user in the group. One of ``member``, ``editor``,
or ``admin``
:type role: string
:returns: the newly created yser
:rtype: dictionary
'''
_check_access('user_invite', context, data_dict)

user_invite_schema = {
'email': [validators.not_empty, unicode]
'email': [validators.not_empty, unicode],
'organization_id': [validators.not_empty],
'role': [validators.not_empty],
}
_, errors = _validate(data_dict, user_invite_schema, context)
if errors:
Expand All @@ -870,6 +876,12 @@ def user_invite(context, data_dict):
data_dict['state'] = ckan.model.State.PENDING
user_dict = _get_action('user_create')(context, data_dict)
user = ckan.model.User.get(user_dict['id'])
member_dict = {
'username': user.id,
'id': data_dict['organization_id'],
'role': data_dict['role']
}
_get_action('organization_member_create')(context, member_dict)
mailer.send_invite(user)
return model_dictize.user_dictize(user, context)
except ValidationError as e:
Expand Down Expand Up @@ -1187,7 +1199,7 @@ def _group_or_org_member_create(context, data_dict, is_org=False):
role = data_dict.get('role')
group_id = data_dict.get('id')
group = model.Group.get(group_id)
result = session.query(model.User).filter_by(name=username).first()
result = model.User.get(username)
if result:
user_id = result.id
else:
Expand Down
2 changes: 1 addition & 1 deletion ckan/logic/auth/create.py
Expand Up @@ -113,7 +113,7 @@ def user_create(context, data_dict=None):
return {'success': True}

def user_invite(context, data_dict=None):
return {'success': False}
return organization_member_create(context, data_dict)

def _check_group_auth(context, data_dict):
# FIXME This code is shared amoung other logic.auth files and should be
Expand Down
13 changes: 7 additions & 6 deletions ckan/model/user.py
Expand Up @@ -181,20 +181,21 @@ def is_deleted(self):
def is_pending(self):
return self.state == core.State.PENDING

def is_in_group(self, group):
return group in self.get_group_ids()
def is_in_group(self, group_id):
return group_id in self.get_group_ids()

def is_in_groups(self, groupids):
def is_in_groups(self, group_ids):
''' Given a list of group ids, returns True if this user is in
any of those groups '''
guser = set(self.get_group_ids())
gids = set(groupids)
gids = set(group_ids)

return len(guser.intersection(gids)) > 0

def get_group_ids(self, group_type=None):
def get_group_ids(self, group_type=None, capacity=None):
''' Returns a list of group ids that the current user belongs to '''
return [g.id for g in self.get_groups(group_type=group_type)]
return [g.id for g in
self.get_groups(group_type=group_type, capacity=capacity)]

def get_groups(self, group_type=None, capacity=None):
import ckan.model as model
Expand Down
5 changes: 4 additions & 1 deletion ckan/tests/lib/test_mailer.py
Expand Up @@ -114,8 +114,10 @@ def test_send_reset_email(self):
# reset link tested in user functional test

def test_send_invite_email(self):
user = model.User.by_name(u'bob')
assert user.reset_key is None, user
# send email
mailer.send_invite(model.User.by_name(u'bob'))
mailer.send_invite(user)

# check it went to the mock smtp server
msgs = self.get_smtp_messages()
Expand All @@ -127,5 +129,6 @@ def test_send_invite_email(self):
expected_body = self.mime_encode(test_msg,
u'bob')
assert expected_body in msg[3], '%r not in %r' % (expected_body, msg[3])
assert user.reset_key is not None, user

# reset link tested in user functional test
47 changes: 23 additions & 24 deletions ckan/tests/logic/test_action.py
Expand Up @@ -562,40 +562,33 @@ def test_12_user_update_errors(self):
for expected_message in test_call['messages']:
assert expected_message[1] in ''.join(res_obj['error'][expected_message[0]])

@mock.patch('ckan.lib.mailer.mail_user')
def test_user_invite(self, mail_user):
@mock.patch('ckan.lib.mailer.send_invite')
def test_user_invite(self, send_invite):
email_username = 'invited_user$ckan'
email = '%s@email.com' % email_username
user_dict = {'email': email}
postparams = '%s=1' % json.dumps(user_dict)
organization_name = 'an_organization'
CreateTestData.create_groups([{'name': organization_name}])
role = 'member'
organization = model.Group.get(organization_name)
params = {'email': email,
'organization_id': organization.id,
'role': role}
postparams = '%s=1' % json.dumps(params)
extra_environ = {'Authorization': str(self.sysadmin_user.apikey)}

res = self.app.post('/api/action/user_invite', params=postparams,
extra_environ=extra_environ)

res_obj = json.loads(res.body)
user = model.User.get(res_obj['result']['id'])
expected_username = email_username.replace('$', '-')
assert res_obj['success'] is True, res_obj
assert user.email == email, (user.email, email)
assert user.name.startswith(expected_username), (user.name, expected_username)
assert user.is_pending(), user
assert user.reset_key is not None, user

@mock.patch('ckan.lib.mailer.send_invite')
def test_user_invite_sends_email(self, send_invite):
email_username = 'invited_user'
email = '%s@email.com' % email_username
user_dict = {'email': email}
postparams = '%s=1' % json.dumps(user_dict)
extra_environ = {'Authorization': str(self.sysadmin_user.apikey)}

res = self.app.post('/api/action/user_invite', params=postparams,
extra_environ=extra_environ)

res_obj = json.loads(res.body)
user = model.User.get(res_obj['result']['id'])
assert res_obj['success'] is True, res_obj
expected_username = email_username.replace('$', '-')
assert user.name.startswith(expected_username), (user.name,
expected_username)
group_ids = user.get_group_ids(capacity=role)
assert organization.id in group_ids, (group_ids, organization.id)
assert send_invite.called
assert send_invite.call_args[0][0].id == res_obj['result']['id']

Expand All @@ -618,8 +611,14 @@ def test_user_invite_should_work_even_if_tried_username_already_exists(self, ran
patcher = mock.patch('ckan.lib.mailer.mail_user')
patcher.start()
email = 'invited_user@email.com'
user_dict = {'email': email}
postparams = '%s=1' % json.dumps(user_dict)
organization_name = 'an_organization'
CreateTestData.create_groups([{'name': organization_name}])
role = 'member'
organization = model.Group.get(organization_name)
params = {'email': email,
'organization_id': organization.id,
'role': role}
postparams = '%s=1' % json.dumps(params)
extra_environ = {'Authorization': str(self.sysadmin_user.apikey)}

usernames = ['first', 'first', 'second']
Expand Down
11 changes: 6 additions & 5 deletions ckan/tests/logic/test_auth.py
@@ -1,3 +1,5 @@
import mock

import ckan.tests as tests
from ckan.logic import get_action
import ckan.model as model
Expand Down Expand Up @@ -49,11 +51,10 @@ def create_user(self, name):


class TestAuthUsers(TestAuth):
def test_only_sysadmins_can_invite_users(self):
username = 'normal_user'
self.create_user(username)

assert not new_authz.is_authorized_boolean('user_invite', {'user': username})
@mock.patch('ckan.logic.auth.create.organization_member_create')
def test_invite_user_delegates_to_organization_member_create(self, organization_member_create):
new_authz.is_authorized_boolean('user_invite', {})
organization_member_create.assert_called()

def test_only_sysadmins_can_delete_users(self):
username = 'username'
Expand Down

0 comments on commit f2f9095

Please sign in to comment.