Handle disabled users

* adding in functionality and validation for
  the handling of disabled users.
* adding tests for said functionality
* changing the templates to change based on the
  user state (existing/new/disabled)
* added some flags for ProjestWithUser to better allow
  safe rerunning of actions.

Change-Id: Ifb9cff8d7d5e6048b566b84e0b295f5c90bf59c1
This commit is contained in:
adrian-turjak 2016-11-23 14:00:26 +13:00
parent e1d25dabec
commit 258b6a54c1
5 changed files with 274 additions and 34 deletions

View File

@ -393,6 +393,15 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin):
'Reporting as invalid.')
return False
if not user.enabled:
self.action.need_token = True
self.action.state = "disabled"
# as they are disabled we'll reset their password
self.set_token_fields(["password"])
self.add_note(
'Existing disabled user with matching email.')
return True
# role_validation
roles = id_manager.get_roles(user, self.project_id)
role_names = {role.name for role in roles}
@ -455,6 +464,39 @@ class NewUserAction(UserNameAction, ProjectMixin, UserMixin):
self.add_note(
'User %s has been created, with roles %s in project %s.'
% (self.username, self.roles, self.project_id))
elif self.action.state == "disabled":
# first re-enable user
try:
user = id_manager.find_user(self.username, self.domain_id)
id_manager.enable_user(user)
except Exception as e:
self.add_note(
"Error: '%s' while re-enabling user: %s with roles: %s" %
(e, self.username, self.roles))
raise
# now add their roles
try:
self._grant_roles(user, self.roles, self.project_id)
except Exception as e:
self.add_note(
"Error: '%s' while attaching user: %s with roles: %s" %
(e, self.username, self.roles))
raise
# and now update their password
try:
id_manager.update_user_password(
user, token_data['password'])
except Exception as e:
self.add_note(
"Error: '%s' while changing password for user: %s" %
(e, self.username))
raise
self.add_note('User %s password has been changed.' % self.username)
self.add_note(
'Existing user %s has been given roles %s in project %s.'
% (self.username, self.roles, self.project_id))
elif self.action.state == "existing":
# Existing action: only add roles.
try:
@ -604,25 +646,31 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
id_manager = user_store.IdentityManager()
user = id_manager.find_user(self.username, self.domain_id)
if user:
if user.email == self.email:
valid = True
self.action.state = "existing"
self.action.need_token = False
self.add_note("Existing user '%s' with matching email." %
self.email)
else:
valid = False
self.add_note("Existing user '%s' with non-matching email." %
self.username)
else:
valid = True
if not user:
self.action.need_token = True
self.set_token_fields(["password"])
self.add_note("No user present with username '%s'." %
self.username)
return True
return valid
if user.email != self.email:
self.add_note("Existing user '%s' with non-matching email." %
self.username)
return False
if not user.enabled:
self.action.state = "disabled"
self.action.need_token = True
self.add_note(
"Existing disabled user '%s' with matching email." %
self.email)
return True
else:
self.action.state = "existing"
self.action.need_token = False
self.add_note("Existing user '%s' with matching email." %
self.email)
return True
def _validate_user_submit(self):
user_id = self.get_cache('user_id')
@ -667,10 +715,11 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
# User validation and checks
user_id = self.get_cache('user_id')
if user_id:
roles_granted = self.get_cache('roles_granted')
if user_id and roles_granted:
self.action.task.cache['user_id'] = user_id
self.add_note("User already created.")
else:
self.add_note("User already setup.")
elif not user_id:
self.action.valid = self._validate_user()
self.action.save()
@ -678,6 +727,8 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
return
self._create_user_for_project()
elif not roles_granted:
self._create_user_for_project()
def _create_user_for_project(self):
id_manager = user_store.IdentityManager()
@ -691,10 +742,17 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
# Generate a temporary password:
password = uuid4().hex + uuid4().hex
user = id_manager.create_user(
name=self.username, password=password,
email=self.email, domain=self.domain_id,
created_on=str(timezone.now()))
user_id = self.get_cache('user_id')
if not user_id:
user = id_manager.create_user(
name=self.username, password=password,
email=self.email, domain=self.domain_id,
created_on=str(timezone.now()))
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
# put user_id into action cache:
self.action.task.cache['user_id'] = user.id
self._grant_roles(user, default_roles, project_id)
except Exception as e:
@ -703,28 +761,77 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
(e, self.username, default_roles))
raise
# put user_id into action cache:
self.action.task.cache['user_id'] = user.id
self.set_cache('user_id', user.id)
self.set_cache('roles_granted', True)
self.add_note(
"New user '%s' created for project %s with roles: %s" %
(self.username, project_id, default_roles))
elif self.action.state == "existing":
try:
user = id_manager.find_user(
self.username, self.domain_id)
user_id = self.get_cache('user_id')
if not user_id:
user = id_manager.find_user(
self.username, self.domain_id)
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
self.action.task.cache['user_id'] = user.id
self._grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
"Error: '%s' while attaching user: %s with roles: %s" %
(e, self.username, default_roles))
"Error: '%s' while granting roles: %s to user: %s" %
(e, default_roles, self.username))
raise
# put user_id into action cache:
self.set_cache('roles_granted', True)
self.add_note(("Existing user '%s' setup on project %s" +
" with roles: %s")
% (self.username, project_id,
default_roles))
elif self.action.state == "disabled":
user_id = self.get_cache('user_id')
if not user_id:
# first re-enable user
try:
user = id_manager.find_user(self.username, self.domain_id)
id_manager.enable_user(user)
except Exception as e:
self.add_note(
"Error: '%s' while re-enabling user: %s" %
(e, self.username))
raise
# and now update their password
# Generate a temporary password:
password = uuid4().hex + uuid4().hex
try:
id_manager.update_user_password(user, password)
except Exception as e:
self.add_note(
"Error: '%s' while changing password for user: %s" %
(e, self.username))
raise
self.add_note(
'User %s password has been changed.' % self.username)
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
self.action.task.cache['user_id'] = user.id
self.set_cache('user_id', user.id)
self.add_note(("Existing user '%s' attached to project %s" +
# now add their roles
roles_granted = self.get_cache('roles_granted')
if not roles_granted:
try:
self._grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
"Error: '%s' while granting user: %s roles: %s" %
(e, self.username, default_roles))
raise
self.set_cache('roles_granted', True)
self.add_note(("Existing user '%s' setup on project %s" +
" with roles: %s")
% (self.username, project_id,
default_roles))
@ -748,7 +855,7 @@ class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
self.action.task.cache['user_id'] = user_id
id_manager = user_store.IdentityManager()
if self.action.state == "default":
if self.action.state in ["default", "disabled"]:
user = id_manager.get_user(user_id)
try:
id_manager.update_user_password(

View File

@ -127,6 +127,67 @@ class ActionTests(TestCase):
self.assertEquals(project.roles[user.id], ['_member_'])
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
FakeManager)
def test_new_user_disabled(self):
"""
Disabled user, valid tenant, no role.
"""
project = mock.Mock()
project.id = 'test_project_id'
project.name = 'test_project'
project.domain = 'default'
project.roles = {}
user = mock.Mock()
user.id = 'user_id_1'
user.name = "test@example.com"
user.email = "test@example.com"
user.domain = 'default'
user.enabled = False
setup_temp_cache({'test_project': project}, {user.id: user})
task = Task.objects.create(
ip_address="0.0.0.0",
keystone_user={
'roles': ['admin', 'project_mod'],
'project_id': 'test_project_id',
'project_domain_id': 'default',
})
data = {
'email': 'test@example.com',
'project_id': 'test_project_id',
'roles': ['_member_'],
'domain_id': 'default',
}
action = NewUserAction(data, task=task, order=1)
action.pre_approve()
self.assertEquals(action.valid, True)
action.post_approve()
self.assertEquals(action.valid, True)
token_data = {'password': '123456'}
action.submit(token_data)
self.assertEquals(action.valid, True)
self.assertEquals(len(tests.temp_cache['users']), 2)
# The new user id in this case will be "user_id_1"
self.assertEquals(
tests.temp_cache['users']["user_id_1"].email,
'test@example.com')
self.assertEquals(
tests.temp_cache['users']["user_id_1"].password,
'123456')
self.assertEquals(
tests.temp_cache['users']["user_id_1"].enabled,
True)
self.assertEquals(project.roles["user_id_1"], ['_member_'])
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
FakeManager)
def test_new_user_existing_role(self):
@ -502,7 +563,65 @@ class ActionTests(TestCase):
'test_project')
self.assertEquals(
task.cache,
{'project_id': 'project_id_1', 'user_id': 'user_id_1'})
{'user_id': 'user_id_1', 'project_id': 'project_id_1'})
token_data = {'password': '123456'}
action.submit(token_data)
self.assertEquals(action.valid, True)
self.assertEquals(
tests.temp_cache['users'][user.id].email,
'test@example.com')
project = tests.temp_cache['projects']['test_project']
self.assertEquals(
sorted(project.roles[user.id]),
sorted(['_member_', 'project_admin',
'project_mod', 'heat_stack_owner']))
@mock.patch('stacktask.actions.models.user_store.IdentityManager',
FakeManager)
def test_new_project_disabled_user(self):
"""
Create a project for a user that is disabled.
"""
user = mock.Mock()
user.id = 'user_id_1'
user.name = "test@example.com"
user.email = "test@example.com"
user.domain = 'default'
user.enabled = False
setup_temp_cache({}, {user.id: user})
task = Task.objects.create(
ip_address="0.0.0.0",
keystone_user={
'roles': ['admin', 'project_mod'],
'project_id': 'test_project_id',
'project_domain_id': 'default',
})
data = {
'domain_id': 'default',
'parent_id': None,
'email': 'test@example.com',
'project_name': 'test_project',
}
action = NewProjectWithUserAction(data, task=task, order=1)
action.pre_approve()
self.assertEquals(action.valid, True)
action.post_approve()
self.assertEquals(action.valid, True)
self.assertEquals(
tests.temp_cache['projects']['test_project'].name,
'test_project')
self.assertEquals(
task.cache,
{'user_id': 'user_id_1', 'project_id': 'project_id_1'})
token_data = {'password': '123456'}
action.submit(token_data)

View File

@ -104,6 +104,12 @@ class IdentityManager(object):
default_project=default_project, created_on=created_on)
return user
def enable_user(self, user):
self.ks_client.users.update(user, enabled=True)
def disable_user(self, user):
self.ks_client.users.update(user, enabled=False)
def update_user_password(self, user, password):
self.ks_client.users.update(user, password=password)

View File

@ -7,4 +7,4 @@ Existing users will be added to the project and do not need to provide additiona
This link will expire automatically after 24 hours. If expired, you will need to request another one from the person who invited you.
Kind regards,
The Openstack team
The Openstack team

View File

@ -134,6 +134,14 @@ class FakeManager(object):
user = self._user_from_id(user)
user.password = password
def enable_user(self, user):
user = self._user_from_id(user)
user.enabled = True
def disable_user(self, user):
user = self._user_from_id(user)
user.enabled = False
def find_role(self, name):
global temp_cache
if temp_cache['roles'].get(name, None):