First usable version..

The object model supports schema, views (aggregation of attributes for
view/edition purpose), attributes and relations.

The addition of relation make it a bit more powerful than the classical
LDAP object model.

To map an LDAP schema, you can use oid URN for attribute names.
This commit is contained in:
Benjamin Dauvergne 2012-04-16 09:51:37 +02:00
parent 2610c6a0cb
commit cc17d0a6d4
5 changed files with 76 additions and 16 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dd_example
*.pyc
local.db

View File

@ -35,3 +35,7 @@ using any attribute with any instance at the database level but schema can be
used to constrain user interface when editing or creating an instance.
Django directory is especially adapted for identity management solutions.
It should be simple to create connector allowing to synchronize a django
directory and an external directory or to provide a virtual view of an external
directory.

View File

@ -1,11 +1,34 @@
from django.contrib.admin import AdminSite, ModelAdmin, site, TabularInline
from django.db import models
from django.utils.translation import gettext_lazy as _
from .schema import get_schemas, schemas_update_subscribe
from models import (Schema, View, ViewMember, Attribute, Principal,
from .models import (Schema, View, ViewMember, Attribute, Principal,
Predicate, Relation, AttributeValue, PrincipalUser)
class AttributeValueInlineAdmin(TabularInline):
model = AttributeValue
class ChildRelationsInlineAdmin(TabularInline):
model = Relation
fk_name = 'object'
verbose_name_plural = _('Child relations')
class ParentRelationsInlineAdmin(TabularInline):
model = Relation
fk_name = 'value'
verbose_name_plural = _('Parent relations')
class PrincipalModelAdmin(ModelAdmin):
inlines = [ AttributeValueInlineAdmin, ChildRelationsInlineAdmin,
ParentRelationsInlineAdmin ]
class ViewMemberInlineAdmin(TabularInline):
model = ViewMember
class ViewModelAdmin(ModelAdmin):
inlines = [ ViewMemberInlineAdmin]
class DirectoryAdmin(AdminSite):
def __init__(self, *args, **kwargs):
super(DirectoryAdmin, self).__init__(*args, **kwargs)
@ -40,7 +63,7 @@ class DirectoryAdmin(AdminSite):
qs = qs.filter(schema=schema)
qs.prefetch_related('attributes', 'child_relations', 'parent_relations')
return qs
model_admin = type(model_admin_name, (ModelAdmin,), dict(queryset=queryset))
model_admin = type(model_admin_name, (PrincipalModelAdmin,), dict(queryset=queryset))
return model_admin
def regenerate_proxy_models(self):
@ -73,21 +96,10 @@ class DirectoryAdmin(AdminSite):
site = DirectoryAdmin()
class AttributeValueInlineAdmin(TabularInline):
model = AttributeValue
class PrincipalModelAdmin(ModelAdmin):
inlines = [ AttributeValueInlineAdmin ]
class ViewMemberInlineAdmin(TabularInline):
model = ViewMember
class ViewModelAdmin(ModelAdmin):
inlines = [ ViewMemberInlineAdmin]
site.register(Principal, PrincipalModelAdmin)
site.register(View, ViewModelAdmin)
for model in (Schema, Attribute, Predicate, Relation,
AttributeValue, PrincipalUser):
site.register(model)
site.register(Principal, PrincipalModelAdmin)
site.register(View, ViewModelAdmin)

View File

@ -276,16 +276,55 @@ class DirectoryUser(User):
class Predicate(models.Model):
name = models.CharField(max_length=128, unique=True)
display_name = models.CharField(max_length=256)
symmetric = models.BooleanField(blank=True)
transitive = models.BooleanField(blank=True)
def __unicode__(self):
return self.display_name or self.name
class RelationManager(models.Manager):
def closure(self, filter_field=None, filter_value=None, predicate=None,
field=None, seen=None):
'''Compute transitive closure of relations'''
kwargs = { filter_field: filter_value }
qs = self.filter(predicate=predicate, **kwargs).values_list(field, flat=True)
traverse = set(qs) - seen
seen |= traverse
if predicate.transitive:
for value in traverse:
self.closure(filter_field=filter_field, filter_value=value, predicate=predicate,
field=field, seen=seen)
return seen
def values1(self, instance, predicate):
return self.closure(filter_field='object', filter_value=instance,
predicate=predicate, field='value', seen=set())
def objects1(self, instance, predicate):
return self.closure(filter_field='value', filter_value=instance,
predicate=predicate, field='object', seen=set())
def values(self, instance, predicate):
seen = self.values1(instance, predicate)
if predicate.symmetric:
seen |= self.objects1(instance, predicate)
return seen
def objects(self, instance, predicate):
seen = self.objects1(instance, predicate)
if predicate.symmetric:
seen |= self.values1(instance, predicate)
return seen
class Relation(models.Model):
'''State a relation between two principals'''
object = models.ForeignKey(Principal, related_name='child_relations')
predicate = models.ForeignKey(Predicate, related_name='relations')
value = models.ForeignKey(Principal, related_name='parent_relations')
class Meta:
unique_together = (('object', 'predicate', 'value'),)
def update_schema(sender, **kwargs):
from . import schema
print 'update schema', kwargs
@ -297,3 +336,4 @@ for model in (Schema, View, ViewMember, Attribute, Predicate, Relation):
post_delete.connect(update_schema, sender=model,
dispatch_uid='schema-rebuild')
import signals

View File

@ -0,0 +1 @@