summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeif Johansson <leifj@sunet.se>2010-07-10 01:25:36 +0200
committerLeif Johansson <leifj@sunet.se>2010-07-10 01:25:36 +0200
commite5c38904d1d7e01781c6622ae5ec3d902494deff (patch)
treed7939ed27c0754e79ee62b4dda0afddcc45c2b60
parent54563defc38d0075bfc448720e4f5805998a824a (diff)
edit+delete+create for names along with linking and an acl mechanism
-rw-r--r--coip/apps/auth/views.py4
-rw-r--r--coip/apps/name/admin.py5
-rw-r--r--coip/apps/name/forms.py22
-rw-r--r--coip/apps/name/models.py92
-rw-r--r--coip/apps/name/views.py72
-rw-r--r--coip/urls.py2
-rw-r--r--templates/apps/name/edit.html11
-rw-r--r--templates/apps/name/name.html6
-rw-r--r--templates/base.html2
9 files changed, 188 insertions, 28 deletions
diff --git a/coip/apps/auth/views.py b/coip/apps/auth/views.py
index 28ff7de..094e974 100644
--- a/coip/apps/auth/views.py
+++ b/coip/apps/auth/views.py
@@ -53,8 +53,8 @@ def accounts_login_federated(request):
profile.save()
#autocreate a few personal namespaces
- lookup('user:'+profile.identifier,True,'#l '+request.user+'#rw')
- lookup(request.user,True,'#l '+request.user+'#rw')
+ lookup('user:'+profile.identifier,True,'system:anyuser#l '+request.user+'#rw')
+ lookup(request.user,True,'system:anyuser#l '+request.user+'#rw')
next = request.session.get("after_login_redirect", None)
if next is not None:
diff --git a/coip/apps/name/admin.py b/coip/apps/name/admin.py
index 477da76..54fc99e 100644
--- a/coip/apps/name/admin.py
+++ b/coip/apps/name/admin.py
@@ -1,5 +1,6 @@
from django.contrib import admin
-from coip.apps.name.models import Name, Attribute
+from coip.apps.name.models import Name, Attribute, NameLink
admin.site.register(Name)
-admin.site.register(Attribute) \ No newline at end of file
+admin.site.register(Attribute)
+admin.site.register(NameLink) \ No newline at end of file
diff --git a/coip/apps/name/forms.py b/coip/apps/name/forms.py
index 62c6fb5..47c7795 100644
--- a/coip/apps/name/forms.py
+++ b/coip/apps/name/forms.py
@@ -4,7 +4,8 @@ Created on Jun 24, 2010
@author: leifj
'''
from django import forms
-from coip.apps.name.models import Name, Attribute
+from coip.apps.name.models import Name, Attribute, NameLink
+from django.forms.fields import BooleanField
class NameForm(forms.ModelForm):
class Meta:
@@ -17,4 +18,21 @@ class AttributeForm(forms.ModelForm):
class NameEditForm(forms.ModelForm):
class Meta:
model = Name
- fields = ['short','description'] \ No newline at end of file
+ fields = ['short','description']
+
+class NewNameForm(forms.ModelForm):
+ class Meta:
+ model = Name
+ fields = ['type','value','short','description']
+
+class NameDeleteForm(forms.Form):
+ recursive = BooleanField(label="Also delete all nodes below this node?",required=False)
+ confirm = BooleanField(label="Confirm")
+
+class NameLinkForm(forms.ModelForm):
+ class Meta:
+ model = NameLink
+ fields = ['dst','type','data']
+
+class NameLinkDeleteForm(forms.Form):
+ confirm = BooleanField(label="Confirm") \ No newline at end of file
diff --git a/coip/apps/name/models.py b/coip/apps/name/models.py
index d68b8bb..3a1e2cc 100644
--- a/coip/apps/name/models.py
+++ b/coip/apps/name/models.py
@@ -7,6 +7,8 @@ from django.db import models
import re
from twisted.python.reflect import ObjectNotFound
from pprint import pprint
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
class Attribute(models.Model):
name = models.CharField(unique=True,max_length=255)
@@ -24,9 +26,9 @@ class Name(models.Model):
type = models.ForeignKey(Attribute, blank=True, null=True,related_name='names')
value = models.CharField(max_length=255)
parent = models.ForeignKey('self', blank=True, null=True,related_name='children')
- partof = models.ForeignKey('self', blank=True, null=True,related_name='parts')
acl = models.TextField(blank=True) # fully-qualified-name '#' rights
short = models.CharField(max_length=64,blank=True)
+ creator = models.ForeignKey(User,blank=True, null=True)
description = models.TextField(blank=True)
timecreated = models.DateTimeField(auto_now_add=True)
lastupdated = models.DateTimeField(auto_now=True)
@@ -61,12 +63,76 @@ class Name(models.Model):
return str
+ def remove(self,recursive=False):
+ if recursive:
+ for c in self.children.all():
+ c.remove(recursive)
+ self.delete()
+
+ # TODO: placeholder for more complete acl system
+ def copy_acl(self):
+ return self.acl
+
+ def link(self,dst,type,data):
+ if not self.has_link(dst,NameLink.part_of,data):
+ link = NameLink(src=self,dst=dst,type=type,data=data)
+ link.save()
+
+ def unlink(self,dst,type,data):
+ try:
+ link = NameLink.objects.get(src=self,dst=dst,type=type,data=data)
+ link.delete()
+ except ObjectDoesNotExist:
+ pass
+
+ def has_link(self,dst,type,data):
+ return NameLink.objects.filter(src=self,dst=dst,type=type,data=data).count() > 0
+
+ def add_ace(self,name,perm):
+ self.link(name,NameLink.access_control,perm)
+
+ def del_ace(self,name,perm):
+ self.unlink(name,NameLink.access_control,perm)
+
+ def list_acl(self):
+ return NameLink.objects.filter(src=self,type=NameLink.access_control)
+
+ def add_partof(self,part):
+ self.link(part,NameLink.part_of,None)
+
def has_permission(self,user,perm):
- return True
+ pprint("has_permission %s %s %s" % (self,user,perm))
+ # TODO: reverse order of test for production system - will spead-up superuser-test and it is cheap
+ #pprint(NameLink.objects.filter(src=self,type=NameLink.access_control,data=perm,dst__memberships__user=user))
+ # user is superuser or acl is on implicit group or user is member of acl group
+ anyuser = lookup("system:anyuser",True)
+ if NameLink.objects.filter(src=self,dst=anyuser,type=NameLink.access_control,data__contains=perm).count() > 0:
+ return True
+ if NameLink.objects.filter(src=self,type=NameLink.access_control,data__contains=perm,dst__memberships__user=user).count() > 0:
+ return True
+
+ if user.is_superuser:
+ return False
+
+ return False #user.is_superuser
def permitted_children(self,user,perm):
return filter(lambda s: s.has_permission(user,perm),self.children.all())
+class NameLink(models.Model):
+ src = models.ForeignKey(Name,related_name='sources')
+ dst = models.ForeignKey(Name,related_name='destinations')
+ type = models.IntegerField()
+ data = models.CharField(max_length=255,blank=True,null=True)
+ timecreated = models.DateTimeField(auto_now_add=True)
+ lastupdated = models.DateTimeField(auto_now=True)
+
+ access_control = 0
+ part_of = 1
+
+ def __unicode__(self):
+ return "%s -> %s [%s %s]" % (self.src,self.dst,self.type,self.data)
+
def roots():
return Name.objects.filter(parent=None)
@@ -76,7 +142,7 @@ def _traverse(name,callable,user,depth):
else:
t = callable(name,depth)
if depth > 0:
- children = [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'#l')]
+ children = [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'l')]
if children:
t['children'] = children
return t
@@ -88,14 +154,15 @@ def traverse(name,callable,user,depth,includeroot=False):
if includeroot:
t = callable(name,depth)
if depth > 0:
- children = [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'#l')]
+ children = [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'l')]
if children:
t['children'] = children
return t
else:
- return [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'#l')]
+ return [_traverse(s,callable,user,depth - 1) for s in name.permitted_children(user,'l')]
-def walkto(root,nameparts,autocreate=False,autoacl='#l'):
+# TODO - remove system user dependency
+def walkto(root,nameparts,autocreate=False,autoacl='l'):
name = None
for n in nameparts:
(a,eq,v) = n.partition('=')
@@ -103,22 +170,25 @@ def walkto(root,nameparts,autocreate=False,autoacl='#l'):
attribute = Attribute.objects.get(name=a)
try:
name = Name.objects.get(parent=root,type=attribute.id,value=v)
- except ObjectNotFound,e:
+ except ObjectDoesNotExist,e:
if autocreate:
- name = Name.objects.create(parent=root,type=attribute.id,value=v,acl=autoacl)
+ name = Name(parent=root,creator=User.objects.get(username='system'),type=attribute.id,value=v,acl=autoacl)
+ name.save()
else:
raise e
else:
try:
name = Name.objects.get(parent=root,type=None,value=a)
- except ObjectNotFound,e:
+ except ObjectDoesNotExist,e:
if autocreate:
- name = Name.objects.create(parent=root,type=None,value=a,acl=autoacl)
+ name = Name(parent=root,creator=User.objects.get(username='system'),type=None,value=a,acl=autoacl)
+ name.save()
else:
raise e
+ root = name
return name
-def lookup(name,autocreate=False,autoacl='#l'):
+def lookup(name,autocreate=False,autoacl='l'):
return walkto(None,nameparts=re.compile('[;:]').split(name),autocreate=autocreate,autoacl=autoacl)
def attribute(a):
diff --git a/coip/apps/name/views.py b/coip/apps/name/views.py
index 6e0132f..a59190b 100644
--- a/coip/apps/name/views.py
+++ b/coip/apps/name/views.py
@@ -10,9 +10,69 @@ from django.http import HttpResponseNotFound, HttpResponseForbidden,\
from django.contrib.auth.decorators import login_required
from coip.multiresponse import respond_to, json_response
from pprint import pprint
-from coip.apps.name.forms import NameEditForm
+from coip.apps.name.forms import NameEditForm, NewNameForm, NameDeleteForm
from twisted.python.reflect import ObjectNotFound
+def delete(request,id):
+ name = None
+ try:
+ name = Name.objects.get(id=id)
+ except ObjectNotFound:
+ return HttpResponseNotFound()
+
+ if not name.has_permission(request.user,'d'):
+ return HttpResponseForbidden()
+
+ if request.method == 'POST':
+ form = NameDeleteForm(request.POST)
+ if form.is_valid():
+ if not form.cleaned_data['confirm']:
+ return HttpResponseRedirect("/name/id/%d" % name.id)
+
+ parent = name.parent
+ if not form.cleaned_data['recursive'] and name.children.count() > 0:
+ return HttpResponseForbidden("Will not delete non-empty node")
+
+ if form.cleaned_data['recursive']:
+ name.remove(True)
+ else:
+ name.remove(False)
+
+ if parent:
+ return HttpResponseRedirect("/name/id/%d" % parent.id)
+ else:
+ return HttpResponseRedirect("/name");
+ else:
+ form = NameDeleteForm()
+
+ return respond_to(request,{'text/html': 'apps/name/edit.html'},{'form': form,'name': name,'formtitle': 'Remove name confirmation' ,'submitname': 'Delete'})
+
+def add(request,id):
+ parent = None
+ if id:
+ try:
+ parent = Name.objects.get(id=id)
+ except ObjectNotFound:
+ return HttpResponseNotFound()
+
+ if id:
+ if not parent.has_permission(request.user,'i'):
+ return HttpResponseForbidden('You are not allowed to create names under '+parent)
+ else:
+ if not request.user.admin:
+ return HttpResponseForbidden('You are not allowed to create names')
+
+ if request.method == 'POST':
+ name = Name(parent=parent,creator=request.user,acl=parent.copy_acl())
+ form = NewNameForm(request.POST,instance=name)
+ if form.is_valid():
+ form.save()
+ return HttpResponseRedirect("/name/id/%d" % name.id)
+ else:
+ form = NewNameForm()
+
+ return respond_to(request,{'text/html': 'apps/name/edit.html'},{'form': form,'name': parent,'formtitle': 'Create new name','submitname': 'Create'})
+
def edit(request,id):
name = None
try:
@@ -20,7 +80,7 @@ def edit(request,id):
except ObjectNotFound:
return HttpResponseNotFound()
- if not name.has_permission(request.user,'#w'):
+ if not name.has_permission(request.user,'w'):
return HttpResponseForbidden()
if request.method == 'POST':
@@ -31,7 +91,7 @@ def edit(request,id):
else:
form = NameEditForm(instance=name)
- return respond_to(request,{'text/html': 'apps/name/edit.html'},{'form': form,'name': name})
+ return respond_to(request,{'text/html': 'apps/name/edit.html'},{'form': form,'name': name,'formtitle': 'Change name','submitname': 'Update'})
def show_root(request):
@@ -48,9 +108,9 @@ def show(request,name):
{'text/html': 'apps/name/name.html'},
{'name': name,
'memberships': name.memberships,
- 'delete': name.has_permission(request.user,'#d'),
- 'insert': name.has_permission(request.user,'#i'),
- 'edit': name.has_permission(request.user,'#w')})
+ 'delete': name.has_permission(request.user,'d'),
+ 'insert': name.has_permission(request.user,'i'),
+ 'edit': name.has_permission(request.user,'w')})
else:
return HttpResponseForbidden()
diff --git a/coip/urls.py b/coip/urls.py
index af60b15..03426ee 100644
--- a/coip/urls.py
+++ b/coip/urls.py
@@ -29,6 +29,8 @@ urlpatterns = patterns('',
(r'^name/(?P<name>[^0-9\/]+)$', 'coip.apps.name.views.show_by_name'),
(r'^name$', 'coip.apps.name.views.show_by_name'),
(r'^name/edit/id/(?P<id>[0-9]+)$', 'coip.apps.name.views.edit'),
+ (r'^name/delete/id/(?P<id>[0-9]+)$', 'coip.apps.name.views.delete'),
+ (r'^name/add/id/(?P<id>[0-9]+)$', 'coip.apps.name.views.add'),
# JSON Tree
(r'^ctree.json$', 'coip.apps.name.views.ctree'),
(r'^ctree/(?P<id>[0-9]+).json$', 'coip.apps.name.views.ctree'),
diff --git a/templates/apps/name/edit.html b/templates/apps/name/edit.html
index bb39478..d18cbec 100644
--- a/templates/apps/name/edit.html
+++ b/templates/apps/name/edit.html
@@ -28,14 +28,19 @@ $(function() {
{% block title %}COIP{% if name %} - {{name.shortname}}{% endif %}{% endblock %}
{% block main %}
<div style="float: left; width: 30%;">
- {% if name and name.parent %}
+ {% if name %}
+ {% if name.parent %}
<a href="/name/id/{{name.parent.id}}">.. (up one level)</a>
+ {% else %}
+ <a href="/name">.. (up one level)</a>
+ {% endif %}
{% endif %}
<div style="height: 100%;" id="tree"></div>
</div>
<div style="float: right; width: 60%; padding-left: 20px;">
<form method="POST">
- <div class="ui-widget-content ui-corner-all infopanel"
+ <div class="ui-widget-content ui-corner-all infopanel">
+ <h3>{{formtitle}}</h3>
<table>
{% for field in form %}
<tr>
@@ -52,7 +57,7 @@ $(function() {
</div>
<br/>
<div class="button">
- <input type="submit" value="Update" />
+ <input type="submit" value="{{submitname}}" />
<input type="button" onClick="document.location='/name/id/{{name.id}}'" value="Cancel"/>
</div>
</form>
diff --git a/templates/apps/name/name.html b/templates/apps/name/name.html
index 6ef3e91..bc20cd6 100644
--- a/templates/apps/name/name.html
+++ b/templates/apps/name/name.html
@@ -28,8 +28,12 @@ $(function() {
{% block title %}COIP{% if name %} - {{name.shortname}}{% endif %}{% endblock %}
{% block main %}
<div style="float: left; width: 30%;">
- {% if name and name.parent %}
+ {% if name %}
+ {% if name.parent %}
<a href="/name/id/{{name.parent.id}}">.. (up one level)</a>
+ {% else %}
+ <a href="/name">.. (up one level)</a>
+ {% endif %}
{% endif %}
<div style="height: 100%;" id="tree"></div>
</div>
diff --git a/templates/base.html b/templates/base.html
index 18172d3..164f67a 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -89,7 +89,7 @@
<div style="float: right; margin-right: 50px;" class="button"><a id="logout" href="/auth/logout/">Logout</a></div>
<div id="linklist">
<ul>
- <li><a href="/user"><span class="ui-icon ui-icon-locked" style="float: left;"></span>{{ profile.display_name }}</a></li>
+ <li><a href="/user/home"><span class="ui-icon ui-icon-locked" style="float: left;"></span>{{ profile.display_name }}</a></li>
</ul>
</div>
{% endif %}