1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

[per-object-permissions] Very basic integration of RLPs into admin interface.

git-svn-id: http://code.djangoproject.com/svn/django/branches/per-object-permissions@3486 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Christopher Long 2006-07-29 16:00:23 +00:00
parent 75c6dc967d
commit 340718bd3a
9 changed files with 562 additions and 1 deletions

View File

@ -0,0 +1,160 @@
/**
* @author clong
*/
//dojo.require("dojo.io.*");
//dojo.require("dojo.event.*");
//dojo.require("dojo.lfx.*");
//dojo.require("dojo.widget.*");
//dojo.require("dojo.json");
function addButtonPressed(obj_ct, obj_id)
{
return;
dojo.io.bind({
url: "/rlp/add/"+obj_ct+"/"+obj_id+"/ajax/",
handler: addCallback,
formNode: dojo.byId('addRLPForm')
});
}
function addCallback(type, data, evt)
{
if (type == 'error')
alert('Error when retrieving data from the server!');
else
{
dictData = dojo.json.evalJson(data);
if(dictData.result == false)
{
//outputError(dictData.text);
return;
}
alert("Success!");
//outputMsg(dictData.text);
//dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [184, 204, 228], 1000).play();
}
}
function applyButtonPressed(id)
{
return;
strArray = id.split("/");
dojo.io.bind({
url: "/rlp/change/"+id+'/ajax/',
handler: editCallback,
formNode: dojo.byId('editRLPForm-'+strArray[1])
});
}
function editCallback(type, data, evt)
{
if (type == 'error')
alert('Error when retrieving data from the server!');
else
{
dictData = dojo.json.evalJson(data);
if(dictData.result == false)
{
outputError(dictData.text);
dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [255, 0, 0], 1000).play();
alert("Error");
return;
}
outputMessage(dictData.text);
dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [184, 204, 228], 1000).play();
}
}
function deleteRLP(id)
{
return;
var confirm_ans = confirm("Are you sure?");
if(confirm_ans)
{
dojo.io.bind({
url: '/rlp/delete/'+id+'/ajax',
handler: deleteCallback,
mimetype: 'text/json'
});
}
}
function deleteCallback(type, data, evt)
{
if (type == 'error')
alert('Error when retrieving data from the server!');
else
{
dictData = dojo.json.evalJson(data);
if(dictData.result == false)
{
//outputError(dictData.text);
dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [255, 0, 0], 1000).play();
return;
}
outputMessage(dictData.text);
var row = dojo.byId('editRLP-'+dictData.id);
var fadeOut = dojo.lfx.fadeOut(row, 1000, null, function(n) {
var table = dojo.byId('rlpTable');
table.deleteRow(row.rowIndex);
});
dojo.lfx.html.highlight(row, [255, 0, 0], 500).play(1500);
fadeOut.play();
}
}
function copyToNew(id)
{
var newForm = document.addRLPForm;
var form = dojo.byId("editRLPForm-"+id);
newForm.owner.selectedIndex = form.owner.selectedIndex;
newForm.perm.selectedIndex = form.perm.selectedIndex;
newForm.negative.checked = form.negative.checked;
}
function outputErrors(errs)
{
var output = genOutput('errors', errs);
dojo.lfx.html.highlight(output, [240, 0, 0], 3000).play();
}
function outputMessage(messages)
{
var output = genOutput('messages', messages);
dojo.lfx.html.highlight(output, [184, 204, 228], 3000).play();
}
function genOutput(id, str)
{
var list = document.createElement("ul");
list.id = id;
var txt = document.createTextNode(str);
var ele = document.createElement("li");
ele.appendChild(txt);
list.appendChild(ele);
var output = dojo.byId('output');
removeChildrenFromNode(output);
output.appendChild(list);
return output;
}
function removeChildrenFromNode(node)
{
while (node.hasChildNodes())
{
node.removeChild(node.firstChild);
}
}
function init()
{
for(var i=0; i<document.forms.length; i++)
{
document.forms[i].reset();
}
}
//dojo.addOnLoad(init);

View File

@ -0,0 +1,138 @@
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User, Group, Permission, RowLevelPermission
from django.db.models import manipulators
from auth import utils
class ChangeRLPManipulator(forms.Manipulator):
def __init__(self, ct=None):
perm_list = []
if ct:
self.ct = ct
perm_list = [(o.id, o.name) for o in self.ct.permission_set.order_by("name")]
obj_list = [('user', User.objects.order_by("username"))]
obj_list.extend([('group', Group.objects.order_by("name"))])
self.fields = (
MultipleObjSelectField(field_name="owner", obj_list=obj_list),
forms.SelectField(field_name="perm", choices=perm_list),
forms.CheckboxField(field_name="negative"),
)
def save(self, new_data):
rlp = RowLevelPermission.objects.get(pk=new_data['id'])
self.original_object = rlp
self.manager = rlp._default_manager
self.opts = rlp._meta
owner = MultipleObjSelectField.returnObject(new_data['owner'])
owner_ct = ContentType.objects.get_for_model(owner)
model_ct = rlp.type_ct
model = model_ct.get_object_for_this_type (pk=rlp.type_id)
perm = Permission.objects.get(pk=new_data['perm'])
field_name_list = ('owner_ct', 'owner_id', 'type_ct', 'type_id', 'permission')
field_data = owner_ct.id
all_data = {'owner_id':owner.id, 'type_ct_id':model_ct.id, 'type_id':model.id, 'permission_id':perm.id}
manipulators.manipulator_validator_unique_together(field_name_list, self.opts, self, field_data, all_data)
rlp.owner = owner
rlp.permission = perm
rlp.negative = new_data['negative']
rlp.save()
return rlp
class AddRLPManipulator(ChangeRLPManipulator):
def __init__(self, obj_instance, ct):
self.ct = ct
self.obj_instance = obj_instance
obj_list = [('user', User.objects.order_by("username"))]
obj_list.extend([('group', Group.objects.order_by("name"))])
perm_list = [(o.id, o.name) for o in self.ct.permission_set.order_by("name")]
self.fields = (
MultipleObjSelectField(field_name="owner", obj_list=obj_list),
forms.SelectMultipleField(field_name="perm", choices=perm_list, size=3),
forms.CheckboxField(field_name="negative"),
)
def save(self, new_data):
owner = MultipleObjSelectField.returnObject(new_data['owner'])
self.manager = RowLevelPermission._default_manager
self.opts = RowLevelPermission._meta
ct = ContentType.objects.get_for_model(owner)
rlp_list = []
for i in new_data.getlist('perm'):
perm = Permission.objects.get(pk=i)
field_name_list = ('owner_ct', 'owner_id', 'type_ct', 'type_id', 'permission')
field_data = ct.id
all_data = {'owner_id':owner.id, 'type_ct_id':self.ct.id, 'type_id':self.obj_instance.id, 'permission_id':perm.id}
manipulators.manipulator_validator_unique_together(field_name_list, self.opts, self, field_data, all_data)
rlp = RowLevelPermission.objects.create_row_level_permission(self.obj_instance, owner, perm, negative=new_data['negative'])
rlp_list.append(rlp)
return rlp_list
#def validate_unique_together(orig_obj, field_name_list, all_data):
#field_list = [opts.get_field(field_name) for field_name in field_name_list]
#kwargs = {}
#for f in field_list:
#field_val = all_data.get(f.attname, None)
#if field_val is None:
#return
#if isinstance(f.rel, ManyToOneRel):
#kwargs['%s__pk' % f.name] = field_val
#else:
#kwargs['%s__iexact' % f.name] = field_val
#try:
#old_obj = self.manager.get(**kwargs)
#except ObjectDoesNotExist:
#return
#if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val():
#pass
#else:
#raise validators.ValidationError, _("%(object)s with this %(type)s already exists for the given %(field)s.") % \
#{'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list(field_name_list[1:], 'and')}
class MultipleObjSelectField(forms.SelectField):
def __init__(self, field_name, obj_list=None,
default_text=None, size=1, is_required=False, validator_list=None,
member_name=None):
if default_text is None:
default_text = "Select an option"
choice_list = [('', default_text)]
for obj, obj_choices in obj_list:
ct = ContentType.objects.get(model__exact=obj)
choice_list.extend([(MultipleObjSelectField.returnKey(o), str(o)+" ("+ct.name.title()+")") for o in obj_choices])
super(MultipleObjSelectField, self).__init__(field_name, choices=choice_list,
size=size, is_required=is_required,
validator_list=validator_list,
member_name=member_name)
def returnObject(data):
data = data.split('-')
ct = ContentType.objects.get(model__exact=data[0])
obj = ct.get_object_for_this_type(pk=data[1])
return obj
def returnKey(obj):
ct = ContentType.objects.get_for_model(obj.__class__)
return ct.model+"-"+str(obj.id)
returnObject = staticmethod(returnObject)
returnKey = staticmethod(returnKey)

View File

@ -64,5 +64,11 @@
</script> </script>
{% endif %} {% endif %}
</div> </div>
</form></div> </form>
{% if rlp_form_list %}
{% include "admin/row_level_permission.html" %}
{% endif %}
</div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,79 @@
{% load i18n admin_modify %}
{% include_admin_script "js/row_level_permission.js" %}
<table id="rlpTable">
<tr class="header">
<th>
{% trans "Owner" %}
</th>
<th>
{% trans "Permission" %}
</th>
<th>
{% trans "Negative" %}
</th>
<th>
{% trans "Options" %}
</th>
</tr>
<form id="addRLPForm" method="POST" name="addRLPForm" action="../../../auth/row_level_permission/add/{{ content_type_id }}/{{ object_id }}/" onSubmit="addButtonPressed({{ obj_ct }}, {{ obj_id }}); return false;">
<tr>
<th colspan=4>
{% trans "Add Permissions" %}
</th>
</tr>
<TBODY>
<tr>
<td>
<p>
{{ new_rlp_form.owner }}
</p>
</td>
<td>
<p>
{{ new_rlp_form.perm }}
</p>
</td>
<td>
<p>
{{ new_rlp_form.negative }}
</p>
</td>
<td>
<input id="addButton" type="submit" value="{% trans 'Add' %}" />
</td>
</tr>
</form>
<tr>
<th colspan=4>
{% trans "Current Permissions" %}
</th>
</tr>
{% if rlp_form_list %}
{% load row_level_permission %}
{% for o in rlp_form_list %}
<tr id="editRLP-{{ o.rlp.id }}">
<form id="editRLPForm-{{ o.rlp.id }}" method="POST" name="editRLPForm-{{ rlp.id }}" action="../../../auth/row_level_permission/{% objref o.rlp %}/change/" onSubmit="applyButtonPressed('{% objref o.rlp %}'); return false;">
<td>
{{ o.form.owner }}
</td>
<td>
{{ o.form.perm }}
</td>
<td>
{{ o.form.negative }}
</td>
<td>
<input id="applyButton-{{ o.rlp.id }}" type="submit" value="{% trans 'Apply' %}" />
<input id="cancelButton-{{ o.rlp.id }}" type="reset" value="{% trans 'Reset' %}"/>
<br/>
<a href="../../../auth/row_level_permission/{% objref o.rlp %}/delete/" onClick="deleteRLP('{% objref o.rlp %}'); return false;" class="deleteLink">{% trans 'Delete' %}</a> |
<a href="" onClick="copyToNew({{ o.rlp.id }}); return false;" class="copyToNewLink">{% trans 'Copy to New' %}</a>
</td>
</form>
</tr>
{% endfor %}
{% else %}
<tr><td>No row level permissions</td></tr>
{% endif %}
</TBODY>
</table>

View File

@ -0,0 +1,37 @@
import sha
from django.contrib.contenttypes.models import ContentType
from django import template
from django.conf import settings
register = template.Library()
#Based off work by Ian Holsman
#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/templatetags/media.py
class objref_class(template.Node):
""" return a object reference to a given object """
def __init__(self, obj):
self.object_name= obj
def render(self, context):
object_id = template.resolve_variable( self.object_name, context )
c_obj = ContentType.objects.get_for_model( object_id ).id
#c_obj = get_content_type_id( object_id )
return "%s/%d/%s" % ( c_obj, object_id.id, sha.new("%s/%d" % (c_obj, object_id.id) + settings.SECRET_KEY).hexdigest())
#Based off work by Ian Holsman
#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/templatetags/media.py
def objref(parser, token):
"""
{% objref <object> %}
"""
bits = token.contents.split()
tok=""
if len(bits) > 2:
raise template.TemplateSyntaxError, "'objref' statements must be 'objref <object>': %s" % token.contents
if len(bits) == 2:
tok = bits[1]
else:
tok = "object"
return objref_class(tok)
register.tag('objref', objref)

View File

@ -28,6 +28,11 @@ urlpatterns = patterns('',
# ('^doc/templates/$', 'django.views.admin.doc.template_index'), # ('^doc/templates/$', 'django.views.admin.doc.template_index'),
('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'), ('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'),
#Row level permissions
('^auth/row_level_permission/(?P<ct_id>\d+)/(?P<rlp_id>\d+)/(?P<hash>\w+)/delete/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.delete_row_level_permission'),
('^auth/row_level_permission/(?P<ct_id>\d+)/(?P<rlp_id>\d+)/(?P<hash>\w+)/change/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.change_row_level_permission'),
('^auth/row_level_permission/add/(?P<ct_id>\d+)/(?P<obj_id>\d+)/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.add_row_level_permission'),
# Add/change/delete/history # Add/change/delete/history
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'), ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'), ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
@ -36,4 +41,5 @@ urlpatterns = patterns('',
('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'), ('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'),
) )
del i18n_view del i18n_view

View File

@ -100,3 +100,14 @@ if docutils_is_available:
for name, urlbase in ROLES.items(): for name, urlbase in ROLES.items():
create_reference_role(name, urlbase) create_reference_role(name, urlbase)
#Based off work by Ian Holsman
#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/misc.py
def verify_objref_hash( content_type_id, object_id, hash ):
import sha
from django.conf import settings
hash_match = sha.new("%s/%s" % (content_type_id, object_id) + settings.SECRET_KEY).hexdigest()
if hash == hash_match:
return True
else:
return False

View File

@ -391,7 +391,26 @@ def change_stage(request, app_label, model_name, object_id):
'object_id': object_id, 'object_id': object_id,
'original': manipulator.original_object, 'original': manipulator.original_object,
'is_popup': request.REQUEST.has_key('_popup'), 'is_popup': request.REQUEST.has_key('_popup'),
'is_row_level_perm': model._meta.row_level_permissions,
}) })
if model._meta.row_level_permissions:
from django.contrib.admin.row_level_perm_manipulator import AddRLPManipulator, ChangeRLPManipulator
model_instance = manipulator.original_object
model_ct = ContentType.objects.get_for_model(model)
rlp_list = model_instance.row_level_permissions.order_by('owner_ct', 'owner_id')
rlp_errors = rlp_new_data = {}
add_rlp_manip = AddRLPManipulator(model_instance, model_ct)
edit_rlp_manip = ChangeRLPManipulator(model_ct)
new_rlp_form = forms.FormWrapper(add_rlp_manip, rlp_new_data, rlp_errors)
rlp_form_list = []
for r in rlp_list:
owner_val = str(r.owner_ct)+"-"+str(r.owner_id)
data = {'id':r.id, 'owner':owner_val, 'perm':r.permission.id, 'negative':r.negative}
rlp_form_list.append({'form':forms.FormWrapper(edit_rlp_manip, data, rlp_errors), 'rlp':r})
rlp_context = {'new_rlp_form':new_rlp_form, 'rlp_form_list':rlp_form_list,}
c.update(rlp_context)
return render_change_form(model, manipulator, c, change=True) return render_change_form(model, manipulator, c, change=True)
change_stage = staff_member_required(never_cache(change_stage)) change_stage = staff_member_required(never_cache(change_stage))

View File

@ -0,0 +1,105 @@
from django.contrib.admin import utils
from django.shortcuts import render_to_response, get_object_or_404
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import RowLevelPermission
from django.contrib.admin.row_level_perm_manipulator import AddRLPManipulator, ChangeRLPManipulator
import simplejson
def delete_row_level_permission(request, ct_id, rlp_id, hash, ajax=None):
msg = {}
if utils.verify_objref_hash(ct_id, rlp_id, hash):
rlp = get_object_or_404(RowLevelPermission, pk=rlp_id)
ct = rlp.type_ct
obj = rlp.type
opts = rlp._meta
if not request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()):
raise PermissionDenied
if not request.user.has_perm(obj._meta.app_label + '.' + obj._meta.get_delete_permission()):
raise PermissionDenied
rlp.delete()
msg = {"result":True, "text":_("Row level permission was successful deleted"), "id":rlp_id}
else:
msg = { 'result':False, 'text': _("row level permission not found (bad hash)" )}
if ajax:
return HttpResponse(simplejson.dumps(msg), 'text/javascript')
request.user.message_set.create(message=msg['text'])
#return HttpResponseRedirect("/edit/%s/%s" % (ct.model, obj.id))
return HttpResponseRedirect("../../../../../../%s/%s/%s" % (obj._meta.app_label, obj._meta.module_name , str(obj.id)))
def add_row_level_permission(request, ct_id, obj_id, ajax=None):
msg = {}
if not request.POST:
msg = { 'result':False, 'text': _("Only POSTs are allowed" )}
if ajax:
return HttpResponse(simplejson.dumps(msg), 'text/javascript')
request.user.message_set.create(message=msg['text'])
return HttpResponseRedirect("/edit/%s/%s" % (obj_type, obj_id))
ct = get_object_or_404(ContentType, pk=ct_id)
obj = get_object_or_404(ct.model_class(), pk=obj_id)
if not request.user.has_perm(obj._meta.app_label + '.' + obj._meta.get_add_permission()):
raise PermissionDenied
manip = AddRLPManipulator(obj, ct)
new_data = request.POST.copy()
manip.do_html2python(new_data)
rlp_list = []
from django.core import validators
try:
rlp_list = manip.save(new_data)
except validators.ValidationError:
msg = {"result":False, "text":_("A row level permission already exists with the specified values.")}
else:
msg = {"result":True, "text":_("Row level permission has successful been added.")}
if not ajax:
request.user.message_set.create(message=msg['text'])
return HttpResponseRedirect("../../../../../%s/%s/%s" % (obj._meta.app_label, obj._meta.module_name , str(obj.id)))
resp_list = []
for rlp in rlp_list:
resp_list.append({"id":rlp.id, "permission":rlp.permission.id})
msg["results"]=resp_list
return HttpResponse(simplejson.dumps(msg), 'text/javascript')
def change_row_level_permission(request, ct_id, rlp_id, hash, ajax=None):
msg = {}
if not request.POST:
msg = { 'result':False, 'text': _("Only POSTs are allowed" )}
if not utils.verify_objref_hash(ct_id, rlp_id, hash):
msg = { 'result':False, 'text': _("row level permission not found (bad hash)" )}
if msg.has_key("result"):
if ajax:
return HttpResponse(simplejson.dumps(msg), 'text/javascript')
request.user.message_set.create(message=msg['text'])
return HttpResponseRedirect("/edit/%s/%s" % (obj_type, obj_id))
obj = get_object_or_404(RowLevelPermission, pk=rlp_id)
opts = obj._meta
#if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
#if not request.user.has_perm(opts.get_change_permission()):
#raise PermissionDenied
manip = ChangeRLPManipulator()
new_data = request.POST.copy()
new_data["id"] = rlp_id
manip.do_html2python(new_data)
from django.core import validators
try:
new_rlp = manip.save(new_data)
except validators.ValidationError:
msg = {"result":False, "text":_("A row level permission already exists with the specified values")}
else:
msg = {"result":True, "text":_("Row level permission has successfully been changed"), "id":rlp_id}
if ajax:
return HttpResponse(simplejson.dumps(msg), 'text/javascript')
request.user.message_set.create(message=msg['text'])
return HttpResponseRedirect("../../../../../../%s/%s/%s" % (new_rlp.type._meta.app_label, new_rlp.type._meta.module_name , str(rlp.type_id)))