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:
parent
e1d25dabec
commit
258b6a54c1
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue