Browse Source

Edit groups by permission

This adds a tab in the User Administrator to allow for assigning permissions by
selecting a set of groups that have a particular permission. Depending on the
use case, it makes sense to be able to edit permissions by modifying them for a
particular group, as well as looking at a particular permission, and the groups
that have that permission.
Jon Natkins 13 years ago
parent
commit
dc134b0215

+ 53 - 0
apps/useradmin/src/useradmin/templates/edit_permissions.mako

@@ -0,0 +1,53 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader, commonfooter
+%>
+<% import urllib %>
+
+<%namespace name="layout" file="layout.mako" />
+${layout.menubar(section='permissions')}
+
+<div class="container-fluid">
+	${commonheader('Edit Permission -- ' + app + ' -- ' + priv, "useradmin", "100px")}
+	<h1>Edit Permission ${app}.${priv}</h1>
+	<form action="${urllib.quote(action)}" method="POST" class="jframe_padded">
+		<fieldset>
+			<legend>
+		        Edit Groups With This Permission
+			</legend>
+        <%def name="render_field(field)">
+			<div class="clearfix">
+				${field.label_tag() | n}
+				<div class="input">
+					${unicode(field) | n}
+				</div>
+				% if len(field.errors):
+					${unicode(field.errors) | n}
+				% endif
+			</div>
+		</%def>
+
+		% for field in form:
+			${render_field(field)}
+		% endfor
+        </fieldset>
+		<div class="actions">
+			<input type="submit" value="Save" class="btn primary"/>
+		</div>
+	</form>
+</div>
+${commonfooter()}

+ 1 - 0
apps/useradmin/src/useradmin/templates/layout.mako

@@ -32,6 +32,7 @@ def is_selected(section, matcher):
 				<ul class="nav">
 					<li><a href="/useradmin/users" class="${is_selected(section, 'users')}">Users</a></li>
 					<li><a href="/useradmin/groups" class="${is_selected(section, 'groups')}">Groups</a></li>
+					<li><a href="/useradmin/permissions" class="${is_selected(section, 'permissions')}">Permissions</a></li>
 				</ul>
 			</div>
 		</div>

+ 93 - 0
apps/useradmin/src/useradmin/templates/list_permissions.mako

@@ -0,0 +1,93 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader, commonfooter
+%>
+<% import urllib %>
+<% from django.utils.translation import ugettext, ungettext, get_language, activate %>
+<% from useradmin.models import group_permissions %>
+<% from django.contrib.auth.models import Group %>
+<% _ = ugettext %>
+
+<%namespace name="layout" file="layout.mako" />
+${commonheader("Hue Permissions", "useradmin", "100px")}
+${layout.menubar(section='permissions')}
+
+<div class="container-fluid">
+	<h1>Hue Permissions</h1>
+	<div class="well">
+			Filter by name: <input id="filterInput"/> <a href="#" id="clearFilterBtn" class="btn">Clear</a>
+	</div>
+      <table class="datatables">
+        <thead>
+          <tr>
+            <th>${_('Application')}</th>
+            <th>${_('Permission')}</th>
+            <th>${_('Groups')}</th>
+			<th>&nbsp;</th>
+          </tr>
+        </head>
+        <tbody>
+        % for perm in permissions:
+          <tr class="permissionRow" data-search="${perm.app}${perm.description}">
+            <td>${perm.app}</td>
+            <td>${perm.description}</td>
+            <td>${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}</td>
+            <td>
+              <a title="Edit groups" class="btn small" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }">Edit</a>
+            </td>
+          </tr>
+        % endfor
+        </tbody>
+      </table>
+
+</div>
+
+	<script type="text/javascript" charset="utf-8">
+		$(document).ready(function(){
+			$(".datatables").dataTable({
+				"bPaginate": false,
+			    "bLengthChange": false,
+				"bInfo": false,
+				"bFilter": false
+			});
+			$(".dataTables_wrapper").css("min-height","0");
+			$(".dataTables_filter").hide();
+
+			$("#filterInput").keyup(function(){
+		        $.each($(".permissionRow"), function(index, value) {
+
+		          if($(value).attr("data-search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+		            $(value).hide(250);
+		          }else{
+		            $(value).show(250);
+		          }
+		        });
+
+		    });
+
+		    $("#clearFilterBtn").click(function(){
+		        $("#filterInput").val("");
+		        $.each($(".file-row"), function(index, value) {
+		            $(value).show(250);
+		        });
+		    });
+
+
+		});
+	</script>
+
+${commonfooter()}

+ 4 - 4
apps/useradmin/src/useradmin/tests.py

@@ -91,10 +91,10 @@ def test_group_permissions():
   assert_true('Hue Users' in response.content)
 
   # And revoke access from the group
-  c.post('/useradmin/groups/edit/test-group',
-         dict(name="test-group",
-         members=[User.objects.get(username="test").pk],
-         permissions=[],
+  c.post('/useradmin/permissions/edit/useradmin/access',
+         dict(app='useradmin',
+         priv='access',
+         groups=[],
          save="Save"), follow=True)
   assert_true(len(GroupPermission.objects.all()) == 0)
   assert_false(get_profile(test_user).has_hue_permission('access','useradmin'))

+ 2 - 0
apps/useradmin/src/useradmin/urls.py

@@ -25,8 +25,10 @@ urlpatterns = patterns('useradmin',
   url(r'^$', 'views.list_users'),
   url(r'^users$', 'views.list_users'),
   url(r'^groups$', 'views.list_groups'),
+  url(r'^permissions$', 'views.list_permissions'),
   url(r'^users/edit/(?P<username>%s)$' % (username_re,), 'views.edit_user'),
   url(r'^groups/edit/(?P<name>%s)$' % (groupname_re,), 'views.edit_group'),
+  url(r'^permissions/edit/(?P<app>.*)/(?P<priv>.*)$', 'views.edit_permission'),
   url(r'^users/new$', 'views.edit_user', name="useradmin.new"),
   url(r'^groups/new$', 'views.edit_group', name="useradmin.new_group"),
   url(r'^users/delete/(?P<username>%s)$' % (username_re,), 'views.delete_user'),

+ 66 - 0
apps/useradmin/src/useradmin/views.py

@@ -44,6 +44,9 @@ def list_users(request):
 def list_groups(request):
   return render("list_groups.mako", request, dict(groups=Group.objects.all()))
 
+def list_permissions(request):
+  return render("list_permissions.mako", request, dict(permissions=HuePermission.objects.all()))
+
 def delete_user(request, username):
   if not request.user.is_superuser:
     raise PopupException("You must be a superuser to delete users.")
@@ -217,6 +220,38 @@ def edit_group(request, name=None):
   return render('edit_group.mako', request,
     dict(form=form, action=request.path, name=name))
 
+def edit_permission(request, app=None, priv=None):
+  """
+  edit_permission(request, app = None, priv = None) -> reply
+
+  @type request:        HttpRequest
+  @param request:       The request object
+  @type app:       string
+  @param app:      Default to None, specifies the app of the privilege
+  @type priv:      string
+  @param priv      Default to None, the action of the privilege
+
+  Only superusers may modify permissions
+  """
+  if not request.user.is_superuser:
+    raise PopupException("You must be a superuser to change permissions.")
+
+  instance = HuePermission.objects.get(app=app, action=priv)
+
+  if request.method == 'POST':
+    form = PermissionsEditForm(request.POST, instance=instance)
+    if form.is_valid():
+      form.save()
+      request.flash.put('Permission information updated')
+      url = urlresolvers.reverse(list_permissions)
+      return format_preserving_redirect(request, url)
+
+  else:
+    form = PermissionsEditForm(instance=instance)
+  return render('edit_permissions.mako', request,
+    dict(form=form, action=request.path, app=app, priv=priv))
+
+
 def _check_remove_last_super(user_obj):
   """Raise an error if we're removing the last superuser"""
   if not user_obj.is_superuser:
@@ -348,6 +383,37 @@ class GroupEditForm(forms.ModelForm):
     for perm in add_permission:
       GroupPermission.objects.create(group=self.instance, hue_permission=perm)
 
+class PermissionsEditForm(forms.ModelForm):
+  """
+  Form to manage the set of groups that have a particular permission.
+  """
+  def __init__(self, *args, **kwargs):
+    super(PermissionsEditForm, self).__init__(*args, **kwargs)
+
+    if self.instance.id:
+      initial_groups = Group.objects.filter(grouppermission__hue_permission=self.instance).order_by('name')
+    else:
+      initial_groups = []
+
+    self.fields["groups"] = _make_model_field(initial_groups, Group.objects.order_by('name'))
+
+  def _compute_diff(self, field_name):
+    current = set(self.fields[field_name].initial_objs)
+    updated = set(self.cleaned_data[field_name])
+    delete = current.difference(updated)
+    add = updated.difference(current)
+    return delete, add
+
+  def save(self):
+    self._save_permissions()
+
+  def _save_permissions(self):
+    delete_group, add_group = self._compute_diff("groups")
+    for group in delete_group:
+      GroupPermission.objects.get(group=group, hue_permission=self.instance).delete()
+    for group in add_group:
+      GroupPermission.objects.create(group=group, hue_permission=self.instance)
+
 def _make_model_field(initial, choices, multi=True):
   """ Creates multiple choice field with given query object as choices. """
   if multi: