This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
veridic/acs/models.py

392 lines
15 KiB
Python

'''
VERIDIC - Towards a centralized access control system
Copyright (C) 2011 Mikael Ates
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from acs.abac.models import AbacRule
class Namespace(models.Model):
'''Namespace
Allow objects of same name
if they are in different namespace'''
name = models.CharField(max_length = 40, unique = True)
def __unicode__(self):
return self.name
class UserAlias(models.Model):
alias = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'))
user = models.ForeignKey(User, null=True, blank=True)
self_admin = models.BooleanField(default=False)
delegated_permissions = models.ManyToManyField('AcsPermission',
verbose_name=_('delegated permissions list'),
blank=True, related_name = 'permissions_list')
class Meta:
verbose_name = _('alias')
verbose_name_plural = _('aliases')
unique_together = ("alias", "namespace")
def __unicode__(self):
if self.user:
return '%s is %s in %s' \
%(self.alias, self.user, self.namespace)
return '%s in %s (alias only)' \
%(self.alias, self.namespace)
class Role(models.Model):
'''Role'''
name = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'),
default = '1')
users = models.ManyToManyField('UserAlias',
verbose_name=_('users'), blank=True)
roles = models.ManyToManyField('Role', symmetrical=False,
verbose_name=_('roles'), blank=True)
class Meta:
verbose_name = _('role')
verbose_name_plural = _('roles')
unique_together = ("name", "namespace")
def __unicode__(self):
return '%s in %s' %(self.name, self.namespace.name)
class Action(models.Model):
'''Action'''
name = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'),
default = '1')
class Meta:
verbose_name = _('action')
verbose_name_plural = _('actions')
unique_together = ("name", "namespace")
def __unicode__(self):
return '%s in %s' %(self.name, self.namespace.name)
class Activity(models.Model):
'''Activity'''
name = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'),
default = '1')
actions = models.ManyToManyField(Action, verbose_name=_('actions'),
blank=True)
activities = models.ManyToManyField('self', symmetrical=False,
verbose_name=_('activities'), blank=True)
class Meta:
verbose_name = _('activity')
verbose_name_plural = _('activities')
unique_together = ("name", "namespace")
def __unicode__(self):
return '%s in %s' %(self.name, self.namespace.name)
class AcsObject(models.Model):
'''
Object
This object is used to determine objects of an application policy
The object of the policy of administration of ACS are the policy
entities.
Then, none of this type of object should be created in the default
namespace or in the global admin view which identifies/point at
objects in the policy of the ACS system
'''
name = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'),
default = '1')
regex = models.CharField(max_length = 100,
verbose_name = _('Regular expression'), null=True)
class Meta:
verbose_name = _('object')
verbose_name_plural = _('objects')
unique_together = ("name", "namespace")
def __unicode__(self):
if self.regex:
return '%s in %s with regex %s' \
% (self.name, self.namespace.name, self.regex)
return '%s in %s' %(self.name, self.namespace.name)
class View(models.Model):
'''
View
A view is a container for objects.
Everything is object
'''
name = models.CharField(max_length = 40)
namespace = models.ForeignKey(Namespace, verbose_name = _('Namespace'),
default = '1')
acs_objects = models.ManyToManyField(AcsObject,
verbose_name=_('acs objects'), blank=True)
views = models.ManyToManyField('self', symmetrical=False,
verbose_name=_('views'), blank=True)
users = models.ManyToManyField(UserAlias,
verbose_name=_('users'), blank=True)
roles = models.ManyToManyField(Role, verbose_name=_('roles'), blank=True)
actions = models.ManyToManyField(Action, verbose_name=_('actions'),
blank=True)
activities = models.ManyToManyField(Activity,
verbose_name=_('activities'), blank=True)
class Meta:
verbose_name = _('view')
verbose_name_plural = _('views')
unique_together = ("name", "namespace")
def __unicode__(self):
return '%s in %s' %(self.name, self.namespace.name)
class AcsPermission(models.Model):
content_type_who = models.ForeignKey(ContentType,
related_name = "who_rbac")
content_type_what = models.ForeignKey(ContentType,
related_name = "what_rbac")
content_type_how = models.ForeignKey(ContentType,
related_name = "how_rbac")
object_id_who = models.PositiveIntegerField()
object_id_what = models.PositiveIntegerField()
object_id_how = models.PositiveIntegerField()
who = generic.GenericForeignKey(ct_field='content_type_who',
fk_field='object_id_who')
what = generic.GenericForeignKey(ct_field='content_type_what',
fk_field='object_id_what')
how = generic.GenericForeignKey(ct_field='content_type_how',
fk_field='object_id_how')
'''
If set it means that the permission is obtained by delegation
'''
permission_source = models.ForeignKey('AcsPermission',
blank=True, null=True, related_name = 'source_permission')
'''
Means that a permission can be delegated
Also that a permission delegated can be delegated
'''
delegable = models.BooleanField(default=False)
'''
Expiration of the permission
That means also expiration of the delegation
'''
expiration_date = models.DateTimeField(blank=True, null=True)
class Meta:
verbose_name = _('permission')
verbose_name_plural = _('permissions')
unique_together = (("content_type_who", "object_id_who",
"content_type_what", "object_id_what",
"content_type_how", "object_id_how"))
def __unicode__(self):
s = _('Permission of %s on %s to perform %s') \
%(self.who, self.what, self.how.name)
if self.permission_source:
s += _(' (delegated)')
if self.delegable:
s += _(' (delegable)')
if self.expiration_date:
s += _(' (expires on %s)' %self.expiration_date)
return s
def save(self, *args, **kwargs):
p = None
t_who = ContentType.objects.get_for_model(self.who)
t_what = ContentType.objects.get_for_model(self.what)
t_how = ContentType.objects.get_for_model(self.how)
'''We check here the unicity, not redundant with unique_together'''
try:
p = AcsPermission.objects.get(content_type_who__pk=t_who.id,
object_id_who=self.who.id,
content_type_what__pk=t_what.id,
object_id_what=self.what.id,
content_type_how__pk=t_how.id,
object_id_how=self.how.id,
)
except MultipleObjectsReturned:
raise Exception('This should never happen')
except ObjectDoesNotExist:
pass
if p:
raise Exception('This permission already exists')
if not isinstance(self.who, (UserAlias, Role)):
raise Exception('Who is %s, not a User or a Role') \
%ContentType.objects.get_for_model(self.who)
if not isinstance(self.how, (Action, Activity)):
raise Exception('How is %s, not an Action or an Activity') \
%ContentType.objects.get_for_model(self.how)
self.content_type_who = ContentType.objects.get_for_model(self.who)
self.object_id_who = self.who.id
self.content_type_what = ContentType.objects.get_for_model(self.what)
self.object_id_what = self.what.id
self.content_type_how = ContentType.objects.get_for_model(self.how)
self.object_id_how = self.how.id
super(AcsPermission, self).save(*args, **kwargs)
class AcsAbacPermission(models.Model):
'''
Who is only a User
When a request is treated:
If who is a role. If there is a matching RBAC rule, the decision is
True.
If there is an ABAC rule set on anybody with a unique predicate role
on that role, the decission is True.
Else a new rule is returned made of permissions set on anybody.
Axiom:
p_RBAC(role, Y, Z)
equiv.
p_ABAC(Anybody, Y, Z, rule: PredicateRole(role))
p_ABAC(user, Y, Z, rule: PredicateRole(role)) means that user may be
granted the access if it has the role.
More powerful than using p_RBAC
Should depreciate AcsPermission and RBAC only processing.
'''
who = models.ForeignKey(UserAlias, related_name = "who_abac",
blank=True, null=True)
content_type_what = models.ForeignKey(ContentType,
related_name = "what_abac")
content_type_how = models.ForeignKey(ContentType,
related_name = "how_abac")
object_id_what = models.PositiveIntegerField()
object_id_how = models.PositiveIntegerField()
what = generic.GenericForeignKey(ct_field='content_type_what',
fk_field='object_id_what')
how = generic.GenericForeignKey(ct_field='content_type_how',
fk_field='object_id_how')
rule = models.ForeignKey(AbacRule, verbose_name = _('AbacRule'))
'''
Expiration of the permission
'''
expiration_date = models.DateTimeField(blank=True, null=True)
class Meta:
verbose_name = _('abac permission')
verbose_name_plural = _('abac permissions')
unique_together = (("rule",
"content_type_what", "object_id_what",
"content_type_how", "object_id_how"))
def __unicode__(self):
if self.who:
s = _('Permission of %s on %s to perform %s if "%s"') \
% (self.who, self.what, self.how.name, self.rule)
else:
s = _('Permission of anyone on %s to perform %s if "%s"') \
% (self.what, self.how.name, self.rule)
if self.expiration_date:
s += _(' (expires on %s)' %self.expiration_date)
return s
def save(self, *args, **kwargs):
p = None
t_what = ContentType.objects.get_for_model(self.what)
t_how = ContentType.objects.get_for_model(self.how)
'''We check here the unicity, not redundant with unique_together'''
try:
if self.who:
p = AcsAbacPermission.objects.get(rule=self.rule,
who=self.who,
content_type_what__pk=t_what.id,
object_id_what=self.what.id,
content_type_how__pk=t_how.id,
object_id_how=self.how.id,
)
else:
p = AcsAbacPermission.objects.get(rule=self.rule,
content_type_what__pk=t_what.id,
object_id_what=self.what.id,
content_type_how__pk=t_how.id,
object_id_how=self.how.id,
)
except MultipleObjectsReturned:
raise Exception('This should never happen')
except ObjectDoesNotExist:
pass
if p:
raise Exception('This permission already exists')
if not isinstance(self.how, (Action, Activity)):
raise Exception('How is %s, not an Action or an Activity') \
%ContentType.objects.get_for_model(self.how)
self.content_type_what = ContentType.objects.get_for_model(self.what)
self.object_id_what = self.what.id
self.content_type_how = ContentType.objects.get_for_model(self.how)
self.object_id_how = self.how.id
super(AcsAbacPermission, self).save(*args, **kwargs)
class Policy(models.Model):
name = models.CharField(max_length = 40, unique=True, null=False)
namespace = models.ForeignKey(Namespace,
verbose_name = _('Namespace of the policy'))
admin_view = models.ForeignKey(View,
verbose_name = _('Admin view of the policy'))
admin_role = models.ForeignKey(Role,
verbose_name = _('Admin role of the policy'),
related_name = 'admin_role')
user_admin_role = models.ForeignKey(Role,
verbose_name = _('User admin role of the policy'),
related_name = 'user_admin_role')
abac_admin_role = models.ForeignKey(Role,
verbose_name = _('ABAC admin role of the policy'),
related_name = 'abac_admin_role')
object_creator_role = models.ForeignKey(Role,
verbose_name = _('Object and View creator role of the policy'),
related_name = 'object_creator_role', null=True)
action_creator_role = models.ForeignKey(Role,
verbose_name = _('Action and Activity creator role of the policy'),
related_name = 'action_creator_role', null=True)
admin_roles = models.ManyToManyField(Role,
verbose_name=_('admin_roles'),
blank=True, related_name = 'admin_roles')
admin_views = models.ManyToManyField(View,
verbose_name=_('admin_views'),
blank=True, related_name = 'admin_views')
class Meta:
verbose_name = _('policy')
verbose_name_plural = _('policies')
def __unicode__(self):
return '%s with namespace %s' %(self.name, self.namespace)