Fixing updating user with no default project

The selector for setting the default project passes on an empty string
if the value is not changed. This is causing an error from keystone as
an empty string is not a valid project ID.

To fix, a couple of things have been done:

First, as user_update is a PATCH, we no longer pass on the default
project as a parameter in the update when using keystone v3
and the value did not change when editing the user.

Second, there is currently no way to unset the default project
using keystoneclient. An entry in the default project select
"select a project" has been removed when the default project is
already set. The entry was selectable and resulted in the
problematic empty string. The current default project is already
selected at load time. The ability to select a different default
project is maintained.

Additionally, keystone v2.0 functionality is not effected, as the
default project id is required in v2.0.

A test was added to cover the case of editing the default project.

Closes-Bug: #1561612
Change-Id: I3c84ee5c7af846b5a58f2dd0342a537d312323cf
(cherry picked from commit 32510d8211)
This commit is contained in:
David Lyle 2016-03-24 12:22:29 -06:00 committed by David Lyle
parent 2b1fe19805
commit 10e6bf57c3
2 changed files with 54 additions and 4 deletions

View File

@ -68,6 +68,7 @@ class BaseUserForm(forms.SelfHandlingForm):
# the user has access to.
user_id = kwargs['initial'].get('id', None)
domain_id = kwargs['initial'].get('domain_id', None)
default_project_id = kwargs['initial'].get('project', None)
try:
if api.keystone.VERSIONS.active >= 3:
@ -82,7 +83,9 @@ class BaseUserForm(forms.SelfHandlingForm):
project_choices.append((project.id, project.name))
if not project_choices:
project_choices.insert(0, ('', _("No available projects")))
elif len(project_choices) > 1:
# TODO(david-lyle): if keystoneclient is fixed to allow unsetting
# the default project, then this condition should be removed.
elif len(project_choices) > 1 and default_project_id is None:
project_choices.insert(0, ('', _("Select a project")))
self.fields['project'].choices = project_choices
@ -227,6 +230,9 @@ class UpdateUserForm(BaseUserForm):
data.pop('domain_id')
data.pop('domain_name')
if not PROJECT_REQUIRED and 'project' not in self.changed_data:
data.pop('project')
if 'description' not in self.changed_data:
data.pop('description')
try:

View File

@ -359,8 +359,7 @@ class UsersViewTests(test.BaseAdminViewTests):
api.keystone.user_update(IsA(http.HttpRequest),
user.id,
email=user.email,
name=user.name,
project=self.tenant.id).AndReturn(None)
name=user.name).AndReturn(None)
self.mox.ReplayAll()
@ -375,6 +374,52 @@ class UsersViewTests(test.BaseAdminViewTests):
self.assertNoFormErrors(res)
@test.create_stubs({api.keystone: ('user_get',
'domain_get',
'tenant_list',
'user_update_tenant',
'user_update_password',
'user_update',
'roles_for_user', )})
def test_update_default_project(self):
user = self.users.get(id="1")
domain_id = user.domain_id
domain = self.domains.get(id=domain_id)
new_project_id = self.tenants.get(id="3").id
api.keystone.user_get(IsA(http.HttpRequest), '1',
admin=True).AndReturn(user)
api.keystone.domain_get(IsA(http.HttpRequest),
domain_id).AndReturn(domain)
if api.keystone.VERSIONS.active >= 3:
api.keystone.tenant_list(
IgnoreArg(), domain=domain.id).AndReturn(
[self.tenants.list(), False])
else:
api.keystone.tenant_list(
IgnoreArg(), user=user.id).AndReturn(
[self.tenants.list(), False])
api.keystone.user_update(IsA(http.HttpRequest),
user.id,
email=user.email,
name=user.name,
project=new_project_id).AndReturn(None)
self.mox.ReplayAll()
formData = {'method': 'UpdateUserForm',
'id': user.id,
'name': user.name,
'description': user.description,
'email': user.email,
'project': new_project_id}
res = self.client.post(USER_UPDATE_URL, formData)
self.assertNoFormErrors(res)
@test.create_stubs({api.keystone: ('user_get',
'domain_get',
'tenant_list',
@ -808,7 +853,6 @@ class UsersViewTests(test.BaseAdminViewTests):
user.id,
email=user.email,
name=user.name,
project=self.tenant.id,
description='changed').AndReturn(None)
self.mox.ReplayAll()