Merge "Clarifying user roles in various places."

This commit is contained in:
Jenkins 2012-06-12 18:29:01 +00:00 committed by Gerrit Code Review
commit e0ecc22728
7 changed files with 84 additions and 17 deletions

View File

@ -262,6 +262,11 @@ def role_list(request):
return keystoneclient(request, admin=True).roles.list() return keystoneclient(request, admin=True).roles.list()
def roles_for_user(request, user, project):
return keystoneclient(request, admin=True).roles.roles_for_user(user,
project)
def add_tenant_user_role(request, tenant_id, user_id, role_id): def add_tenant_user_role(request, tenant_id, user_id, role_id):
""" Adds a role for a user on a tenant. """ """ Adds a role for a user on a tenant. """
return keystoneclient(request, admin=True).roles.add_user_role(user_id, return keystoneclient(request, admin=True).roles.add_user_role(user_id,
@ -289,6 +294,7 @@ def get_default_role(request):
try: try:
roles = keystoneclient(request, admin=True).roles.list() roles = keystoneclient(request, admin=True).roles.list()
except: except:
roles = []
exceptions.handle(request) exceptions.handle(request)
for role in roles: for role in roles:
if role.id == default or role.name == default: if role.id == default or role.name == default:

View File

@ -4,6 +4,7 @@ from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from horizon import api from horizon import api
from horizon import exceptions
from horizon import tables from horizon import tables
from ..users.tables import UsersTable from ..users.tables import UsersTable
@ -70,16 +71,16 @@ class TenantFilterAction(tables.FilterAction):
class TenantsTable(tables.DataTable): class TenantsTable(tables.DataTable):
id = tables.Column('id', verbose_name=_('Id'))
name = tables.Column('name', verbose_name=_('Name')) name = tables.Column('name', verbose_name=_('Name'))
description = tables.Column(lambda obj: getattr(obj, 'description', None), description = tables.Column(lambda obj: getattr(obj, 'description', None),
verbose_name=_('Description')) verbose_name=_('Description'))
id = tables.Column('id', verbose_name=_('Project ID'))
enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True) enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True)
class Meta: class Meta:
name = "tenants" name = "tenants"
verbose_name = _("Projects") verbose_name = _("Projects")
row_actions = (EditLink, UsageLink, ViewMembersLink, ModifyQuotasLink, row_actions = (ViewMembersLink, EditLink, UsageLink, ModifyQuotasLink,
DeleteTenantsAction) DeleteTenantsAction)
table_actions = (TenantFilterAction, CreateLink, DeleteTenantsAction) table_actions = (TenantFilterAction, CreateLink, DeleteTenantsAction)
@ -97,12 +98,29 @@ class RemoveUserAction(tables.BatchAction):
api.keystone.remove_tenant_user(request, tenant_id, user_id) api.keystone.remove_tenant_user(request, tenant_id, user_id)
class ProjectUserRolesColumn(tables.Column):
def get_raw_data(self, user):
request = self.table._meta.request
try:
roles = api.keystone.roles_for_user(request,
user.id,
self.table.kwargs["tenant_id"])
except:
roles = []
exceptions.handle(request,
_("Unable to retrieve role information."))
return ", ".join([role.name for role in roles])
class TenantUsersTable(UsersTable): class TenantUsersTable(UsersTable):
roles = ProjectUserRolesColumn("roles", verbose_name=_("Roles"))
class Meta: class Meta:
name = "tenant_users" name = "tenant_users"
verbose_name = _("Users For Project") verbose_name = _("Users For Project")
table_actions = (RemoveUserAction,) table_actions = (RemoveUserAction,)
row_actions = (RemoveUserAction,) row_actions = (RemoveUserAction,)
columns = ("name", "email", "id", "roles", "enabled")
class AddUserAction(tables.LinkAction): class AddUserAction(tables.LinkAction):
@ -122,3 +140,4 @@ class AddUsersTable(UsersTable):
verbose_name = _("Add New Users") verbose_name = _("Add New Users")
table_actions = () table_actions = ()
row_actions = (AddUserAction,) row_actions = (AddUserAction,)
columns = ("name", "email", "id", "enabled")

View File

@ -67,12 +67,17 @@ class TenantsViewTests(test.BaseAdminViewTests):
def test_modify_users(self): def test_modify_users(self):
self.mox.StubOutWithMock(api.keystone, 'tenant_get') self.mox.StubOutWithMock(api.keystone, 'tenant_get')
self.mox.StubOutWithMock(api.keystone, 'user_list') self.mox.StubOutWithMock(api.keystone, 'user_list')
self.mox.StubOutWithMock(api.keystone, 'roles_for_user')
api.keystone.tenant_get(IgnoreArg(), self.tenant.id, admin=True) \ api.keystone.tenant_get(IgnoreArg(), self.tenant.id, admin=True) \
.AndReturn(self.tenant) .AndReturn(self.tenant)
api.keystone.user_list(IsA(http.HttpRequest)) \ api.keystone.user_list(IsA(http.HttpRequest)) \
.AndReturn(self.users.list()) .AndReturn(self.users.list())
api.keystone.user_list(IsA(http.HttpRequest), self.tenant.id) \ api.keystone.user_list(IsA(http.HttpRequest), self.tenant.id) \
.AndReturn([self.user]) .AndReturn([self.user])
api.keystone.roles_for_user(IsA(http.HttpRequest),
self.user.id,
self.tenant.id) \
.AndReturn(self.roles.list())
self.mox.ReplayAll() self.mox.ReplayAll()
url = reverse('horizon:syspanel:projects:users', url = reverse('horizon:syspanel:projects:users',
args=(self.tenant.id,)) args=(self.tenant.id,))

View File

@ -72,6 +72,13 @@ class CreateUserForm(BaseUserForm):
required=False, required=False,
widget=forms.PasswordInput(render_value=False)) widget=forms.PasswordInput(render_value=False))
tenant_id = forms.ChoiceField(label=_("Primary Project")) tenant_id = forms.ChoiceField(label=_("Primary Project"))
role_id = forms.ChoiceField(label=_("Role"))
def __init__(self, *args, **kwargs):
roles = kwargs.pop('roles')
super(CreateUserForm, self).__init__(*args, **kwargs)
role_choices = [(role.id, role.name) for role in roles]
self.fields['role_id'].choices = role_choices
# We have to protect the entire "data" dict because it contains the # We have to protect the entire "data" dict because it contains the
# password and confirm_password strings. # password and confirm_password strings.
@ -89,12 +96,10 @@ class CreateUserForm(BaseUserForm):
_('User "%s" was successfully created.') _('User "%s" was successfully created.')
% data['name']) % data['name'])
try: try:
default_role = api.keystone.get_default_role(request) api.add_tenant_user_role(request,
if default_role: data['tenant_id'],
api.add_tenant_user_role(request, new_user.id,
data['tenant_id'], data['role_id'])
new_user.id,
default_role.id)
except: except:
exceptions.handle(request, exceptions.handle(request,
_('Unable to add user to primary project.')) _('Unable to add user to primary project.'))

View File

@ -103,12 +103,12 @@ class UsersTable(tables.DataTable):
("true", True), ("true", True),
("false", False) ("false", False)
) )
id = tables.Column('id', verbose_name=_('ID'))
name = tables.Column('name', verbose_name=_('User Name')) name = tables.Column('name', verbose_name=_('User Name'))
email = tables.Column('email', verbose_name=_('Email')) email = tables.Column('email', verbose_name=_('Email'))
# Default tenant is not returned from Keystone currently. # Default tenant is not returned from Keystone currently.
#default_tenant = tables.Column('default_tenant', #default_tenant = tables.Column('default_tenant',
# verbose_name=_('Default Project')) # verbose_name=_('Default Project'))
id = tables.Column('id', verbose_name=_('User ID'))
enabled = tables.Column('enabled', verbose_name=_('Enabled'), enabled = tables.Column('enabled', verbose_name=_('Enabled'),
status=True, status=True,
status_choices=STATUS_CHOICES, status_choices=STATUS_CHOICES,

View File

@ -46,7 +46,8 @@ class UsersViewTests(test.BaseAdminViewTests):
@test.create_stubs({api: ('user_create', @test.create_stubs({api: ('user_create',
'tenant_list', 'tenant_list',
'add_tenant_user_role'), 'add_tenant_user_role'),
api.keystone: ('get_default_role',)}) api.keystone: ('get_default_role',
'role_list')})
def test_create(self): def test_create(self):
user = self.users.get(id="1") user = self.users.get(id="1")
role = self.roles.first() role = self.roles.first()
@ -58,6 +59,7 @@ class UsersViewTests(test.BaseAdminViewTests):
user.password, user.password,
self.tenant.id, self.tenant.id,
True).AndReturn(user) True).AndReturn(user)
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()).AndReturn(role) api.keystone.get_default_role(IgnoreArg()).AndReturn(role)
api.add_tenant_user_role(IgnoreArg(), self.tenant.id, user.id, role.id) api.add_tenant_user_role(IgnoreArg(), self.tenant.id, user.id, role.id)
@ -68,17 +70,22 @@ class UsersViewTests(test.BaseAdminViewTests):
'email': user.email, 'email': user.email,
'password': user.password, 'password': user.password,
'tenant_id': self.tenant.id, 'tenant_id': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': user.password} 'confirm_password': user.password}
res = self.client.post(USER_CREATE_URL, formData) res = self.client.post(USER_CREATE_URL, formData)
self.assertNoFormErrors(res) self.assertNoFormErrors(res)
self.assertMessageCount(success=1) self.assertMessageCount(success=1)
@test.create_stubs({api: ('tenant_list',)}) @test.create_stubs({api: ('tenant_list',),
api.keystone: ('role_list', 'get_default_role')})
def test_create_with_password_mismatch(self): def test_create_with_password_mismatch(self):
user = self.users.get(id="1") user = self.users.get(id="1")
api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list()) api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
self.mox.ReplayAll() self.mox.ReplayAll()
@ -87,17 +94,22 @@ class UsersViewTests(test.BaseAdminViewTests):
'email': user.email, 'email': user.email,
'password': user.password, 'password': user.password,
'tenant_id': self.tenant.id, 'tenant_id': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': "doesntmatch"} 'confirm_password': "doesntmatch"}
res = self.client.post(USER_CREATE_URL, formData) res = self.client.post(USER_CREATE_URL, formData)
self.assertFormError(res, "form", None, ['Passwords do not match.']) self.assertFormError(res, "form", None, ['Passwords do not match.'])
@test.create_stubs({api: ('tenant_list',)}) @test.create_stubs({api: ('tenant_list',),
api.keystone: ('role_list', 'get_default_role')})
def test_create_validation_for_password_too_short(self): def test_create_validation_for_password_too_short(self):
user = self.users.get(id="1") user = self.users.get(id="1")
api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list()) api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
self.mox.ReplayAll() self.mox.ReplayAll()
@ -107,6 +119,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'email': user.email, 'email': user.email,
'password': 'four', 'password': 'four',
'tenant_id': self.tenant.id, 'tenant_id': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': 'four'} 'confirm_password': 'four'}
res = self.client.post(USER_CREATE_URL, formData) res = self.client.post(USER_CREATE_URL, formData)
@ -115,11 +128,15 @@ class UsersViewTests(test.BaseAdminViewTests):
res, "form", 'password', res, "form", 'password',
['Password must be between 8 and 18 characters.']) ['Password must be between 8 and 18 characters.'])
@test.create_stubs({api: ('tenant_list',)}) @test.create_stubs({api: ('tenant_list',),
api.keystone: ('role_list', 'get_default_role')})
def test_create_validation_for_password_too_long(self): def test_create_validation_for_password_too_long(self):
user = self.users.get(id="1") user = self.users.get(id="1")
api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list()) api.tenant_list(IgnoreArg(), admin=True).AndReturn(self.tenants.list())
api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list())
api.keystone.get_default_role(IgnoreArg()) \
.AndReturn(self.roles.first())
self.mox.ReplayAll() self.mox.ReplayAll()
@ -129,6 +146,7 @@ class UsersViewTests(test.BaseAdminViewTests):
'email': user.email, 'email': user.email,
'password': 'MoreThanEighteenChars', 'password': 'MoreThanEighteenChars',
'tenant_id': self.tenant.id, 'tenant_id': self.tenant.id,
'role_id': self.roles.first().id,
'confirm_password': 'MoreThanEighteenChars'} 'confirm_password': 'MoreThanEighteenChars'}
res = self.client.post(USER_CREATE_URL, formData) res = self.client.post(USER_CREATE_URL, formData)

View File

@ -18,7 +18,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging import operator
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -33,9 +33,6 @@ from .forms import CreateUserForm, UpdateUserForm
from .tables import UsersTable from .tables import UsersTable
LOG = logging.getLogger(__name__)
class IndexView(tables.DataTableView): class IndexView(tables.DataTableView):
table_class = UsersTable table_class = UsersTable
template_name = 'syspanel/users/index.html' template_name = 'syspanel/users/index.html'
@ -85,3 +82,20 @@ class CreateView(forms.ModalFormView):
'confirm_password')) 'confirm_password'))
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
return super(CreateView, self).dispatch(*args, **kwargs) return super(CreateView, self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(CreateView, self).get_form_kwargs()
try:
roles = api.keystone.role_list(self.request)
except:
redirect = reverse("horizon:syspanel:users:index")
exceptions.handle(self.request,
_("Unable to retrieve user roles."),
redirect=redirect)
roles.sort(key=operator.attrgetter("id"))
kwargs['roles'] = roles
return kwargs
def get_initial(self):
default_role = api.keystone.get_default_role(self.request)
return {'role_id': getattr(default_role, "id", None)}