From aad109b9a49d3c2144d522ef34dff88fdff32b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Ates?= Date: Wed, 21 Sep 2011 18:44:19 +0200 Subject: [PATCH] [attribute_aggregator] application of attribute management extracted from acs --- acs/attribute_aggregator/__init__.py | 0 acs/attribute_aggregator/core.py | 191 ++++++++++ acs/attribute_aggregator/ldap_sources.py | 132 +++++++ acs/attribute_aggregator/mapping.py | 171 +++++++++ acs/attribute_aggregator/models.py | 397 ++++++++++++++++++++ acs/attribute_aggregator/signals.py | 30 ++ acs/attribute_aggregator/xacml_constants.py | 296 +++++++++++++++ 7 files changed, 1217 insertions(+) create mode 100644 acs/attribute_aggregator/__init__.py create mode 100644 acs/attribute_aggregator/core.py create mode 100644 acs/attribute_aggregator/ldap_sources.py create mode 100644 acs/attribute_aggregator/mapping.py create mode 100644 acs/attribute_aggregator/models.py create mode 100644 acs/attribute_aggregator/signals.py create mode 100644 acs/attribute_aggregator/xacml_constants.py diff --git a/acs/attribute_aggregator/__init__.py b/acs/attribute_aggregator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/acs/attribute_aggregator/core.py b/acs/attribute_aggregator/core.py new file mode 100644 index 0000000..bf97edd --- /dev/null +++ b/acs/attribute_aggregator/core.py @@ -0,0 +1,191 @@ +''' + VERIDIC Project - 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 . +''' + + +import time +import datetime +import logging +import re + +from django.db import transaction +from django.core.exceptions import ObjectDoesNotExist + +from attribute_aggregator.xacml_constants import * +from attribute_aggregator.mapping import ATTRIBUTE_MAPPING + + +logger = logging.getLogger('attribute_aggregator') + + +def iso8601_to_datetime(date_string): + ''' + Convert a string formatted as an ISO8601 date into a time_t value. + This function ignores the sub-second resolution. + ''' + m = re.match(r'(\d+-\d+-\d+T\d+:\d+:\d+)(?:\.\d+)?Z?$', date_string) + if not m: + raise ValueError('Invalid ISO8601 date') + tm = time.strptime(m.group(1)+'Z', "%Y-%m-%dT%H:%M:%SZ") + return datetime.datetime.fromtimestamp(time.mktime(tm)) + + +def get_def_name_from_name_and_ns_of_attribute(name, namespace): + if not name or not namespace: + return None + for def_name, content in ATTRIBUTE_MAPPING.items(): + if namespace in content["definitions"].keys(): + if name in content["definitions"][namespace]["identifiers"]: + return def_name + if name in content["definitions"][namespace]["friendly_name"]: + return def_name + return None + + +def get_attribute_name_in_namespace(definition, namespace): + if not definition or not namespace: + return None + if definition in ATTRIBUTE_MAPPING: + if namespace in ATTRIBUTE_MAPPING[definition]["definitions"]: + return ATTRIBUTE_MAPPING[definition]\ + ["definitions"][namespace]["identifiers"][0] + return None + + +def convert_from_string(definition_name, value): + if not definition_name in ATTRIBUTE_MAPPING: + return None + type_ = ATTRIBUTE_MAPPING[definition_name]['type'] + + if type_ == ACS_XACML_DATATYPE_STRING: + return value + elif type_ == ACS_XACML_DATATYPE_BOOLEAN: + if value in ('True', 'true', 'Vrai', 'vrai'): + return True + return False + elif type_ == ACS_XACML_DATATYPE_INTEGER: + try: + return int(value) + except: + return None + elif type_ == ACS_XACML_DATATYPE_DOUBLE: + try: + return float(value) + except: + return None + elif type_ == ACS_XACML_DATATYPE_TIME: + try: + return time.strptime(value,"%h:%m:%s") #12:15:00 + except: + return None + elif type_ == ACS_XACML_DATATYPE_DATE: + try: + return time.strptime(value,"%d/%b/%Y") #28/01/1982 + except: + return None + elif type_ == ACS_XACML_DATATYPE_DATETIME: + try: + return iso8601_to_datetime(value) + except: + return None + elif type_ == ACS_XACML_DATATYPE_RFC822NAME: # email + r = re.compile(\ + '[a-zA-Z0-9+_\-\.]+@[0-9a-zA-Z][.-0-9a-zA-Z]*.[a-zA-Z]+') + if r.search(value): + return value + else: + return None + elif type_ == ACS_XACML_DATATYPE_IPADDRESS: # x.x.x.x + r = re.compile(\ + '(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})') + if r.search(value): + return value + else: + return None + return None + + +@transaction.commit_manually +def load_or_create_user_profile(user=None, no_cleanup=False): + ''' + If the user has a profile, remove assertions outdated and return + profile, else create a new one. + + If cleanup: expiration_date < now() remove assertion data from profile + + If no_cleanup: return profile if any without removing outdated + assertions + ''' + from attribute_aggregator.models import UserAttributeProfile + profile = None + try: + if user: + try: + logger.debug('load_or_create_user_profile: \ + search profile for %s' \ + % user) + profile = UserAttributeProfile.objects.get(user=user) + except ObjectDoesNotExist: + profile = UserAttributeProfile(user=user) + profile.save() + logger.info('load_or_create_user_profile: \ + profile with id %s for user %s created' \ + % (str(profile.id), user)) + else: + if no_cleanup: + logger.debug('load_or_create_user_profile: Existing user \ + profile %s for %s - No cleanup' % (profile, user)) + else: + profile.cleanup() + profile.save() + elif not user: + profile = UserAttributeProfile() + profile.save() + logger.debug('load_or_create_user_profile: \ + profile with id %s for an anonymous user created' \ + % str(profile.id)) + except Exception, err: + transaction.rollback() + logger.error('load_or_create_user_profile: An error occured %s' \ + % str(err)) + return None + else: + transaction.commit() + logger.debug('load_or_create_user_profile: everything successfully \ + performed') + return profile + + +def get_user_alias_in_source(user, source): + from attribute_aggregator.models import UserAliasInSource + try: + alias = UserAliasInSource.objects.get(user=user, source=source) + return alias.name + except: + return None + + +def set_user_alias_in_source(user, source, name): + from attribute_aggregator.models import UserAliasInSource + try: + alias, c = UserAliasInSource.objects.get_or_create(user=user, + source=source, name=name) + alias.save() + return alias + except: + return None diff --git a/acs/attribute_aggregator/ldap_sources.py b/acs/attribute_aggregator/ldap_sources.py new file mode 100644 index 0000000..c5ee287 --- /dev/null +++ b/acs/attribute_aggregator/ldap_sources.py @@ -0,0 +1,132 @@ +''' + 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 . +''' + + +import logging +import ldap + +from attribute_aggregator.core import get_user_alias_in_source, \ + get_attribute_name_in_namespace + + +logger = logging.getLogger('acs') + + +def get_all_attributes(user, definitions=None, **kwargs): + ''' + Dictionnary format: + attributes = dict() + data_from_source = list() + a1 = dict() + a1['definition'] = definition_name + a1['name'] = attribute_name_in_ns + a1['namespace'] = ns_name + a1['values'] = list_of_values + data_from_source.append(a1) + ... + data_from_source.append(a2) + attributes[source_name] = data_from_source + + First attempt on 'definition' key. + Else, definition is searched by 'name' and 'namespece' keys. + ''' + if not user: + logger.error('get_all_attributes: No user provided') + return None + logger.debug('get_all_attributes: Searching attributes for user %s' % user) + + from attribute_aggregator.models import LdapSource + sources = LdapSource.objects.all() + if not sources: + logger.debug('get_all_attributes: No LDAP source configured') + return None + + attributes = {} + + for source in sources: + logger.debug('get_all_attributes: The LDAP source is known as %s' \ + % source.name) + + identifier = get_user_alias_in_source(user, source) + if not identifier: + logger.error('get_all_attributes: No user identifier known into that \ + source') + else: + logger.debug('get_all_attributes: the user is known as %s in source %s' \ + % (identifier, source.name)) + + try: + l = ldap.open(source.server) + l.protocol_version = ldap.VERSION3 + username = source.user + password = source.password + if username and password: + l.simple_bind(username, password) + except ldap.LDAPError, err: + logger.error('get_all_attributes: an error occured at binding due \ + to %s' % err) + else: + base_dn = source.base + search_scope = ldap.SCOPE_SUBTREE + retrieve_attributes = None + if definitions: + retrieve_attributes = [\ + get_attribute_name_in_namespace(definition, + 'X500') for definition in definitions] + dn = ldap.dn.explode_dn(identifier, + flags=ldap.DN_FORMAT_LDAPV3) + search_filter = dn[0] + logger.debug('get_all_attributes: rdn is %s' % search_filter) + + data = [] + try: + ldap_result_id = l.search(base_dn, search_scope, + search_filter, retrieve_attributes) + result_type, result_data = l.result(ldap_result_id, 0) + logger.debug('get_all_attributes: result %s %s' % (result_type, + result_data)) + for d, dic in result_data: + logger.debug('get_all_attributes: found %s' % d) + if d == identifier: + logger.debug('get_all_attributes: Attributes are %s' \ + % dic) + for key in dic.keys(): + attr = {} + attr['name'] = key + attr['values'] = [\ + a.decode('utf-8'). \ + encode('ascii', 'ignore') \ + for a in dic[key]] + attr['namespace'] = 'X500' + data.append(attr) + except ldap.LDAPError, err: + logger.error('get_all_attributes: an error occured at searching \ + due to %s' % err) + else: + if not data: + logger.error('get_all_attributes: no attribute found') + else: + attributes[source.name] = data + + logger.debug('get_all_attributes: the attributes returned are %s' % attributes) + return attributes + + +def get_listed_attributes(user, definitions, **kwargs): + return get_all_attributes(user, definitions=definitions, **kwargs) diff --git a/acs/attribute_aggregator/mapping.py b/acs/attribute_aggregator/mapping.py new file mode 100644 index 0000000..0326d6a --- /dev/null +++ b/acs/attribute_aggregator/mapping.py @@ -0,0 +1,171 @@ +''' + VERIDIC Project - 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 . +''' + +from django.utils.translation import ugettext as _ + +ATTRIBUTE_MAPPING = { + +"unique_ID": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("Unique Identifier"), + "definitions": { + "X500": { + "identifiers": + [ + "uid", + ], + "friendly_name" : + [] + }, + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims": { + "identifiers": + [ +"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/\ + privatepersonalidentifier", + ], + "friendly_name": + [], + } + } +}, + +"surname": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("Surname"), + "definitions": { + "X500": { + "identifiers": + [ + "sn", + "2.5.4.4", + ], + "friendly_name" : + [] + }, + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims": { + "identifiers": + [ +"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname", + "Last Name" + ], + "friendly_name": + [], + } + } +}, + +"firstname": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("First Name"), + "definitions": { + "X500": { + "identifiers": + [ + "givenName", + ], + "friendly_name" : + [] + }, + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims": { + "identifiers": + [ +"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", + ], + "friendly_name": + [], + } + } +}, + +"displayname": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("Display Name"), + "definitions": { + "X500": { + "identifiers": + [ + "displayName", + ], + "friendly_name" : + [] + }, + } +}, + +"email": { + "type": "urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name", + "friendly_name": _("Email Address"), + "definitions": { + "X500": { + "identifiers": + [ + "mail", + ], + "friendly_name" : + [] + }, + } +}, + +"title": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("Title"), + "definitions": { + "X500": { + "identifiers": + [ + "title", + ], + "friendly_name" : + [] + }, + } +}, + +"age": { + "type": "http://www.w3.org/2001/XMLSchema#integer", + "friendly_name": _("Title"), + "definitions": { + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims": { + "identifiers": + [ +"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth", + ], + "friendly_name" : + [] + }, + } +}, + +"nationality": { + "type": "http://www.w3.org/2001/XMLSchema#string", + "friendly_name": _("Nationality"), + "definitions": { + "ISO7501-1": { + "identifiers": + [ + "Nationality", + ], + "friendly_name" : + [] + }, + } +}, + +} diff --git a/acs/attribute_aggregator/models.py b/acs/attribute_aggregator/models.py new file mode 100644 index 0000000..b3820c6 --- /dev/null +++ b/acs/attribute_aggregator/models.py @@ -0,0 +1,397 @@ +''' + VERIDIC Project - 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 . +''' + + +import datetime +import logging + +from cPickle import loads, dumps + +from django.utils.translation import ugettext as _ +from django.db import models +from django.contrib.auth.models import User + +from attribute_aggregator.signals import any_attributes_call, \ + listed_attributes_call +from attribute_aggregator.mapping import ATTRIBUTE_MAPPING +from attribute_aggregator.core import convert_from_string, \ + get_def_name_from_name_and_ns_of_attribute, iso8601_to_datetime + + +logger = logging.getLogger('attribute_aggregator') + + +class AttributeSource(models.Model): + name = models.CharField(max_length = 200, unique=True) + + def __unicode__(self): + return self.name + + def get_source_instance(self): + try: + return self.ldapsource + except: + pass + return None + + +def get_source_from_name(name): + try: + return AttributeSource.objects.get(name=name) + except: + return None + + +class LdapSource(AttributeSource): + server = models.CharField(max_length=200, unique=True) + user = models.CharField(max_length=200, blank=True, null=True) + password = models.CharField(max_length=200, blank=True, null=True) + base = models.CharField(max_length=200) + port = models.IntegerField(default=389) + ldaps = models.BooleanField(default=False) + certificate = models.TextField(blank=True) + is_auth_backend = models.BooleanField(default=False) + + +class UserAliasInSource(models.Model): + name = models.CharField(max_length = 200, unique=True) + source = models.ForeignKey(AttributeSource, + verbose_name = _('Attribute Source')) + user = models.ForeignKey(User, related_name='user_alias_in_source') + + +class AttributeData: + + def __init__(self, definition, values=None, source=None, + expiration_date=None): + self.definition = definition + self.values = list() + for value in values: + self.values.append(value) + if isinstance(source, AttributeSource): + self.source_id = source.id + else: + self.source_id = -1 + '''ISO8601''' + try: + iso8601_to_datetime(expiration_date) + self.expiration_date = expiration_date + except: + self.expiration_date = None + + def get_definition(self): + return self.definition + + def get_full_definition(self): + if not self.definition in ATTRIBUTE_MAPPING: + return None + return ATTRIBUTE_MAPPING[self.definition] + + def get_exptime_in_iso8601(self): + return self.expiration_date + + def get_exptime_in_datetime(self): + return iso8601_to_datetime(self.expiration_date) + + def set_exptime_in_iso8601(self, expiration_date): + try: + iso8601_to_datetime(expiration_date) + self.expiration_date = expiration_date + except: + self.expiration_date = None + self.save() + return self.expiration_date + + def set_exptime_in_datetime(self, expiration_date): + try: + self.expiration_date = expiration_date.isoformat() + except: + self.expiration_date = None + self.save() + return self.expiration_date + + def get_values(self): + return self.values + + def get_source(self): + try: + return AttributeSource.objects.get(pk=self.source_id) + except: + return None + + def get_source_id(self): + return self.source_id + + def add_value(self, value): + try: + self.values.append(value) + self.save() + return 0 + except: + return -1 + + def remove_value(self, value): + try: + self.values.remove(value) + self.save() + return 0 + except: + return -1 + + def does_expire(self): + if self.expiration_date: + return self.expiration_date + else: + return 0 + + def __unicode__(self): + s = "%s with values %s" % (self.get_definition(), + [v for v in self.get_values()]) + source = self.get_source() + if source: + s += " from %s" % str(source) + if self.does_expire(): + s += " (Expires on %s)" % self.does_expire() + return s + + +class UserAttributeProfile(models.Model): + user = models.OneToOneField(User, null=True, blank=True, + related_name='user_attribute_profile') + data = models.TextField(null=True, blank=True) + + def add_data(self, data): + if not isinstance(data, AttributeData): + return -1 + try: + l = None + if not self.data: + l = list() + else: + l = loads(str(self.data)) + l.append(data) + self.data = dumps(l) + except: + return -1 + return 0 + + def remove_data(self, position): + try: + l = loads(str(self.data)) + res = l.pop(position) + self.data = dumps(l) + return res + except: + return None + + def get_all_data(self): + try: + l = loads(str(self.data)) + return l + except: + return None + + def get_data_of_definition(self, definition, in_list=None): + l = None + if in_list: + l = in_list + else: + l = self.get_all_data() + if not l: + return None + return [d for d in l if d.get_definition() == definition] + + def get_freshest_data_of_definition(self, definition): + l = self.get_data_of_definition(definition) + if not l: + return None + l.sort(key=lambda x: x.expiration_date, reverse=True) + return l[0] + + def get_data_of_source(self, source, in_list=None): + l = None + if in_list: + l = in_list + else: + l = self.get_all_data() + if not l or not isinstance(source, AttributeSource): + return None + return [d for d in l if d.get_source_id() == source.id] + + def get_data_of_source_by_name(self, source): + l = self.get_all_data() + if not l: + return None + s = None + try: + s = AttributeSource.objects.get(name=source) + except: + return None + return [d for d in l if d.get_source_id() == s.id] + + def get_data_of_definition_and_source(self, definition, source): + return self.get_data_of_definition(definition, + in_list=self.get_data_of_source(source)) + + def get_data_of_definition_and_source_by_name(self, definition, source): + return self.get_data_of_definition(definition, + in_list=self.get_data_of_source_by_name(source)) + + def load_by_dic(self, dictionnary): + ''' + Dictionnary format: + attributes = dict() + data_from_source = list() + a1 = dict() + a1['definition'] = definition_name + a1['name'] = attribute_name_in_ns + a1['namespace'] = ns_name + a1['values'] = list_of_values + data_from_source.append(a1) + ... + data_from_source.append(a2) + attributes[source_name] = data_from_source + + First attempt on 'definition' key. + Else, definition is searched by 'name' and 'namespece' keys. + ''' + if not dictionnary: + logger.error('load_by_dic: \ + Missing profile or dictionnary') + return -1 + for source_name in dictionnary: + logger.debug('load_by_dic: loading from source with name: %s' \ + % source_name) + source = get_source_from_name(source_name) + if source: + logger.debug('load_by_dic: attributes: %s' \ + % str(dictionnary[source_name])) + for attribute in dictionnary[source_name]: + if (not ('definition' in attribute \ + and attribute['definition'] \ + in ATTRIBUTE_MAPPING) \ + and not('name' in attribute \ + and 'namespace' in attribute)) \ + or not 'values' in attribute: + logger.warn('load_by_dic: \ + missing data to treat %s' % str(attribute)) + else: + definition = None + if 'definition' in attribute \ + and attribute['definition'] \ + in ATTRIBUTE_MAPPING: + definition = attribute['definition'] + else: + definition = \ + get_def_name_from_name_and_ns_of_attribute(\ + attribute['name'], + attribute['namespace']) + if not definition: + logger.warn('load_by_dic: \ + unable to find definition for %s' \ + % str(attribute)) + else: + logger.debug('load_by_dic: \ + definition %s found' % definition) + + expiration_date = None + if 'expiration_date' in attribute: + logger.debug('load_by_dic: expire at %s' \ + % attribute['expiration_date']) + try: + iso8601_to_datetime(\ + attribute['expiration_date']) + expiration_date = \ + attribute['expiration_date'] + logger.debug('load_by_dic: expiration \ + date has the ISO8601 format') + except: + logger.warn('load_by_dic: expiration \ + date has not the ISO8601 format') + if not expiration_date: + expiration_date = \ + datetime.datetime.now().isoformat() + + values = [value for value in attribute['values'] \ + if convert_from_string(definition, value)] + + if self.add_data(AttributeData(\ + definition, + values=values, + source=source, + expiration_date=expiration_date)) == 0: + logger.debug('load_by_dic: \ + attribute successfully added') + else: + logger.warn('load_by_dic: \ + error addind attribute') + else: + logger.critical('load_by_dic: \ + The source with name %s providing attributes %s \ + is unknown of the system' \ + % (str(source_name), str(dictionnary[source_name]))) + self.save() + return 0 + + def load_greedy(self): + if self.user: + attributes_provided = any_attributes_call.send(sender=None, + user=self.user) + for attrs in attributes_provided: + logger.info('load_greedy: \ + attributes_call connected to function %s' % \ + attrs[0].__name__) + logger.info('load_greedy: \ + attributes provided are %s' %str(attrs[1])) + self.load_by_dic(attrs[1]) + + def load_listed_attributes(self, definitions): + if self.user: + attributes_provided = listed_attributes_call.send(sender=None, + user=self.user, definitions=definitions) + for attrs in attributes_provided: + logger.info('load_listed_attributes: \ + attributes_call connected to function %s' % \ + attrs[0].__name__) + logger.info('load_listed_attributes: \ + attributes provided are %s' %str(attrs[1])) + self.load_by_dic(attrs[1]) + + def cleanup(self): + l = self.get_all_data() + if not l: + return 0 + now = datetime.datetime.now() + self.data = dumps([d for d in l if d.expiration_date \ + and d.get_exptime_in_datetime() > now]) + self.save() + + def __unicode__(self): + s = None + if self.user: + s = "Profile of %s" % self.user + else: + s = "Anonymous profile" + if not self.get_all_data(): + s += " is empty." + return s + s += " that contains:" + for d in self.get_all_data(): + s = s + "\n\t" + d.__unicode__() + return s diff --git a/acs/attribute_aggregator/signals.py b/acs/attribute_aggregator/signals.py new file mode 100644 index 0000000..a53c7dc --- /dev/null +++ b/acs/attribute_aggregator/signals.py @@ -0,0 +1,30 @@ +''' + 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 . +''' + + +from django.dispatch import Signal +from attribute_aggregator.ldap_sources import get_all_attributes, \ + get_listed_attributes + + +any_attributes_call = Signal(providing_args = ["user"]) +listed_attributes_call = Signal(providing_args = ["user", "definitions"]) + +any_attributes_call.connect(get_all_attributes) +listed_attributes_call.connect(get_listed_attributes) diff --git a/acs/attribute_aggregator/xacml_constants.py b/acs/attribute_aggregator/xacml_constants.py new file mode 100644 index 0000000..9710849 --- /dev/null +++ b/acs/attribute_aggregator/xacml_constants.py @@ -0,0 +1,296 @@ +''' + 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 . +''' + +''' + (Core - 3958) For the sake of improved interoperability, it is RECOMMENDED + that all time references be in UTC time. + (Core - 3960) For doubles, XACML SHALL use the conversions described in + [IEEE754] +''' + +#RENAME me to XACML constants! + +from django.utils.translation import ugettext as _ + + +ACS_XACML_DATATYPE_STRING = "http://www.w3.org/2001/XMLSchema#string" +ACS_XACML_DATATYPE_BOOLEAN = "http://www.w3.org/2001/XMLSchema#boolean" +ACS_XACML_DATATYPE_INTEGER = "http://www.w3.org/2001/XMLSchema#integer" +ACS_XACML_DATATYPE_DOUBLE = "http://www.w3.org/2001/XMLSchema#double" +ACS_XACML_DATATYPE_TIME = "http://www.w3.org/2001/XMLSchema#time" +ACS_XACML_DATATYPE_DATE = "http://www.w3.org/2001/XMLSchema#date" +ACS_XACML_DATATYPE_DATETIME = "http://www.w3.org/2001/XMLSchema#dateTime" +#ACS_XACML_DATATYPE_ANYURI = "http://www.w3.org/2001/XMLSchema#anyURI" +#ACS_XACML_DATATYPE_HEXBINARY = "http://www.w3.org/2001/XMLSchema#hexBinary" +#ACS_XACML_DATATYPE_BASE64BINARY = \ +# "http://www.w3.org/2001/XMLSchema#base64Binary" +#ACS_XACML_DATATYPE_DAYTIMEDURATION = \ +# "http://www.w3.org/2001/XMLSchema#dayTimeDuration" +#ACS_XACML_DATATYPE_YEARMONTHDURATION = \ +# "http://www.w3.org/2001/XMLSchema#yearMonthDuration" + + +''' +An ITU-T Rec.X.520 Distinguished Name. +The valid syntax for such a name is described in IETF RFC 2253 +"Lightweight Directory Access Protocol (v3): UTF-8 String Representation of +Distinguished Names". +''' +ACS_XACML_DATATYPE_X500NAME = \ + "urn:oasis:names:tc:xacml:1.0:data-type:x500Name" +''' +An electronic mail address. The valid syntax for such a name is described +in IETF RFC 2821, Section 4.1.2, +Command Argument Syntax, under the term "Mailbox". +''' +ACS_XACML_DATATYPE_RFC822NAME = \ + "urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name" +''' +The syntax SHALL be: +ipAddress = address [ "/" mask ] [ ":" [ portrange ] ] +For an IPv4 address, the address and mask are formatted in accordance with the syntax for a +"host" in IETF RFC 2396 "Uniform Resource Identifiers (URI): Generic Syntax", section 3.2. +For an IPv6 address, the address and mask are formatted in accordance with the syntax for an +"ipv6reference" in IETF RFC 2732 "Format for Literal IPv6 Addresses in URL's". (Note that an +IPv6 address or mask, in this syntax, is enclosed in literal "[" "]" brackets.) +''' +ACS_XACML_DATATYPE_IPADDRESS = \ + "urn:oasis:names:tc:xacml:2.0:data-type:ipAddress" +''' +The syntax SHALL be: +dnsName = hostname [ ":" portrange ] +The hostname is formatted in accordance with IETF RFC 2396 "Uniform Resource Identifiers +(URI): Generic Syntax", section 3.2, except that a wildcard "*" may be used in the left-most +component of the hostname to indicate "any subdomain" under the domain specified to its right. +For both the "urn:oasis:names:tc:xacml:2.0:data-type:ipAddress" and +"urn:oasis:names:tc:xacml:2.0:data-type:dnsName" data-types, the port or port range syntax +SHALL be +portrange = portnumber | "-"portnumber | portnumber"-"[portnumber] +where "portnumber" is a decimal port number. If the port number is of the form "-x", where "x" is +a port number, then the range is all ports numbered "x" and below. If the port number is of the +form "x-", then the range is all ports numbered "x" and above. [This syntax is taken from the Java +SocketPermission.] +''' +ACS_XACML_DATATYPE_DNSNAME = \ + "urn:oasis:names:tc:xacml:2.0:data-type:dnsName" +ACS_XACML_DATATYPE_XPATHEXPRESSION = \ + "urn:oasis:names:tc:xacml:3.0:data-type:xpathExpression" + +XACML_DATA_TYPE = ( + (ACS_XACML_DATATYPE_STRING, _('String')), + (ACS_XACML_DATATYPE_BOOLEAN, _('Boolean')), + (ACS_XACML_DATATYPE_INTEGER, _('Integer')), + (ACS_XACML_DATATYPE_DOUBLE, _('Double')), + (ACS_XACML_DATATYPE_TIME, _('Time')), + (ACS_XACML_DATATYPE_DATE, _('Date')), + (ACS_XACML_DATATYPE_DATETIME, _('Date and Time')), +# (ACS_XACML_DATATYPE_ANYURI, _('Any URI')), +# (ACS_XACML_DATATYPE_HEXBINARY, _('Hex Binary')), +# (ACS_XACML_DATATYPE_BASE64BINARY, _('Base64 Binary')), +# (ACS_XACML_DATATYPE_DAYTIMEDURATION, _('Day Time Duration')), +# (ACS_XACML_DATATYPE_YEARMONTHDURATION, _('Year Month Duration')), +# (ACS_XACML_DATATYPE_X500NAME, _('X500 name')), + (ACS_XACML_DATATYPE_RFC822NAME, _('email address')), + (ACS_XACML_DATATYPE_IPADDRESS, _('IP address')), +# (ACS_XACML_DATATYPE_DNSNAME, _('DNS name')), +# (ACS_XACML_DATATYPE_XPATHEXPRESSION, _('XPATH expression')), +) + +ACS_XACML_COMPARISON_EQUALITY_STRING = \ + "urn:oasis:names:tc:xacml:1.0:function:string-equal" +ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE = \ + "urn:oasis:names:tc:xacml:3.0:function:string-equal-ignore-case" +ACS_XACML_COMPARISON_EQUALITY_BOOLEAN = \ + "urn:oasis:names:tc:xacml:1.0:function:boolean-equal" +ACS_XACML_COMPARISON_EQUALITY_INTEGER = \ + "urn:oasis:names:tc:xacml:1.0:function:integer-equal" +ACS_XACML_COMPARISON_EQUALITY_DOUBLE = \ + "urn:oasis:names:tc:xacml:1.0:function:double-equal" +ACS_XACML_COMPARISON_EQUALITY_DATE = \ + "urn:oasis:names:tc:xacml:1.0:function:date-equal" +ACS_XACML_COMPARISON_EQUALITY_TIME = \ + "urn:oasis:names:tc:xacml:1.0:function:time-equal" +ACS_XACML_COMPARISON_EQUALITY_DATETIME = \ + "urn:oasis:names:tc:xacml:1.0:function:dateTime-equal" +ACS_XACML_COMPARISON_EQUALITY_DAYTIMEDURATION = \ + "urn:oasis:names:tc:xacml:3.0:function:dayTimeDuration-equal" +ACS_XACML_COMPARISON_EQUALITY_YEARMONTHDURATION = \ + "urn:oasis:names:tc:xacml:3.0:function:yearMonthDuration-equal" +ACS_XACML_COMPARISON_EQUALITY_ANYURI = \ + "urn:oasis:names:tc:xacml:1.0:function:anyURI-equal" +ACS_XACML_COMPARISON_EQUALITY_X500NAME = \ + "urn:oasis:names:tc:xacml:1.0:function:x500Name-equal" +ACS_XACML_COMPARISON_EQUALITY_RFC822NAME = \ + "urn:oasis:names:tc:xacml:1.0:function:rfc822Name-equal" +ACS_XACML_COMPARISON_EQUALITY_IPADDRESS = \ + "urn:oasis:names:tc:xacml:1.0:function:ipAddress-equal" +ACS_XACML_COMPARISON_EQUALITY_HEXBINARY = \ + "urn:oasis:names:tc:xacml:1.0:function:hexBinary-equal" +ACS_XACML_COMPARISON_EQUALITY_BASE64BINARY = \ + "urn:oasis:names:tc:xacml:1.0:function:base64Binary-equal" + +XACML_COMPARISON_EQUALITY = ( + ACS_XACML_COMPARISON_EQUALITY_STRING, + ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE, + ACS_XACML_COMPARISON_EQUALITY_BOOLEAN, + ACS_XACML_COMPARISON_EQUALITY_INTEGER, + ACS_XACML_COMPARISON_EQUALITY_DOUBLE, + ACS_XACML_COMPARISON_EQUALITY_DATE, + ACS_XACML_COMPARISON_EQUALITY_TIME, + ACS_XACML_COMPARISON_EQUALITY_DATETIME, +# ACS_XACML_COMPARISON_EQUALITY_DAYTIMEDURATION, _('Day Time Duration equality')), +# ACS_XACML_COMPARISON_EQUALITY_YEARMONTHDURATION, _('Year Month Duration equality')), +# ACS_XACML_COMPARISON_EQUALITY_ANYURI, _('Any URI equality')), +# ACS_XACML_COMPARISON_EQUALITY_X500NAME, _('X500 name equality')), + ACS_XACML_COMPARISON_EQUALITY_RFC822NAME, + ACS_XACML_COMPARISON_EQUALITY_IPADDRESS, +# ACS_XACML_COMPARISON_EQUALITY_HEXBINARY, _('Hex binary equality')), +# ACS_XACML_COMPARISON_EQUALITY_BASE64BINARY, _('base 64 binary equality')), +) + +XACML_COMPARISON_EQUALITY_TYPE = ( + (ACS_XACML_COMPARISON_EQUALITY_STRING, _('String equality')), + (ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE, _('String equality ignoring case')), + (ACS_XACML_COMPARISON_EQUALITY_BOOLEAN, _('Boolean equality')), + (ACS_XACML_COMPARISON_EQUALITY_INTEGER, _('Integer equality')), + (ACS_XACML_COMPARISON_EQUALITY_DOUBLE, _('Double equality')), + (ACS_XACML_COMPARISON_EQUALITY_DATE, _('Date equality')), + (ACS_XACML_COMPARISON_EQUALITY_TIME, _('Time equality')), + (ACS_XACML_COMPARISON_EQUALITY_DATETIME, _('DateTime equality')), +# (ACS_XACML_COMPARISON_EQUALITY_DAYTIMEDURATION, _('Day Time Duration equality')), +# (ACS_XACML_COMPARISON_EQUALITY_YEARMONTHDURATION, _('Year Month Duration equality')), +# (ACS_XACML_COMPARISON_EQUALITY_ANYURI, _('Any URI equality')), +# (ACS_XACML_COMPARISON_EQUALITY_X500NAME, _('X500 name equality')), + (ACS_XACML_COMPARISON_EQUALITY_RFC822NAME, _('email equality')), + (ACS_XACML_COMPARISON_EQUALITY_IPADDRESS, _('IP address equality')), +# (ACS_XACML_COMPARISON_EQUALITY_HEXBINARY, _('Hex binary equality')), +# (ACS_XACML_COMPARISON_EQUALITY_BASE64BINARY, _('base 64 binary equality')), +) + +XACML_COMPARISON_EQUALITY_TYPE_DIC = { + ACS_XACML_COMPARISON_EQUALITY_STRING: _('String equality'), + ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE: _('String equality ignoring case'), + ACS_XACML_COMPARISON_EQUALITY_BOOLEAN: _('Boolean equality'), + ACS_XACML_COMPARISON_EQUALITY_INTEGER: _('Integer equality'), + ACS_XACML_COMPARISON_EQUALITY_DOUBLE: _('Double equality'), + ACS_XACML_COMPARISON_EQUALITY_DATE: _('Date equality'), + ACS_XACML_COMPARISON_EQUALITY_TIME: _('Time equality'), + ACS_XACML_COMPARISON_EQUALITY_DATETIME: _('DateTime equality'), +# ACS_XACML_COMPARISON_EQUALITY_DAYTIMEDURATION: _('Day Time Duration equality'), +# ACS_XACML_COMPARISON_EQUALITY_YEARMONTHDURATION: _('Year Month Duration equality'), +# ACS_XACML_COMPARISON_EQUALITY_ANYURI: _('Any URI equality'), +# ACS_XACML_COMPARISON_EQUALITY_X500NAME: _('X500 name equality'), + ACS_XACML_COMPARISON_EQUALITY_RFC822NAME: _('email equality'), + ACS_XACML_COMPARISON_EQUALITY_IPADDRESS: _('IP address equality'), +# ACS_XACML_COMPARISON_EQUALITY_HEXBINARY: _('Hex binary equality'), +# ACS_XACML_COMPARISON_EQUALITY_BASE64BINARY: _('base 64 binary equality'), +} + +ACS_XACML_COMPARISON_INTEGER_GRT = \ + "urn:oasis:names:tc:xacml:1.0:function:integer-greater-than" +ACS_XACML_COMPARISON_INTEGER_GRT_OE = \ + "urn:oasis:names:tc:xacml:1.0:function:integer-greater-than-or-equal" +ACS_XACML_COMPARISON_INTEGER_LT = \ + "urn:oasis:names:tc:xacml:1.0:function:integer-less-than" +ACS_XACML_COMPARISON_INTEGER_LT_OE = \ + "urn:oasis:names:tc:xacml:1.0:function:integer-less-than-or-equal" +ACS_XACML_COMPARISON_DOUBLE_GRT = \ + "urn:oasis:names:tc:xacml:1.0:function:double-greater-than" +ACS_XACML_COMPARISON_DOUBLE_GRT_OE = \ + "urn:oasis:names:tc:xacml:1.0:function:double-greater-than-or-equal" +ACS_XACML_COMPARISON_DOUBLE_LT = \ + "urn:oasis:names:tc:xacml:1.0:function:double-less-than" +ACS_XACML_COMPARISON_DOUBLE_LT_OE = \ + "urn:oasis:names:tc:xacml:1.0:function:double-less-than-or-equal" + +ACS_XACML_COMPARISON_GRT = ( + ACS_XACML_COMPARISON_INTEGER_GRT, + ACS_XACML_COMPARISON_DOUBLE_GRT, +) +ACS_XACML_COMPARISON_GRT_OE = ( + ACS_XACML_COMPARISON_INTEGER_GRT_OE, + ACS_XACML_COMPARISON_DOUBLE_GRT_OE, +) +ACS_XACML_COMPARISON_LT = ( + ACS_XACML_COMPARISON_INTEGER_LT, + ACS_XACML_COMPARISON_DOUBLE_LT, +) +ACS_XACML_COMPARISON_LT_OE = ( + ACS_XACML_COMPARISON_INTEGER_LT_OE, + ACS_XACML_COMPARISON_DOUBLE_LT_OE, +) + +ACS_XACML_COMPARISON = ACS_XACML_COMPARISON_GRT \ + + ACS_XACML_COMPARISON_GRT_OE \ + + ACS_XACML_COMPARISON_LT \ + + ACS_XACML_COMPARISON_LT_OE + +XACML_COMPARISON_DIFF_TYPE = ( + (ACS_XACML_COMPARISON_INTEGER_GRT, _('Integer 1 greater than integer 2')), + (ACS_XACML_COMPARISON_INTEGER_GRT_OE, _('Integer 1 greater than or equal integer 2')), + (ACS_XACML_COMPARISON_INTEGER_LT, _('Integer 1 less than integer 2')), + (ACS_XACML_COMPARISON_INTEGER_LT_OE, _('Integer 1 less than or equal integer 2')), + (ACS_XACML_COMPARISON_DOUBLE_GRT, _('Double 1 greater than double 2')), + (ACS_XACML_COMPARISON_DOUBLE_GRT_OE, _('Double 1 greater than or equal double 2')), + (ACS_XACML_COMPARISON_DOUBLE_LT, _('Double 1 less than double 2')), + (ACS_XACML_COMPARISON_DOUBLE_LT_OE, _('Double 1 less than or equal double 2')), +) + +XACML_COMPARISON_DIFF_TYPE_DIC = { + ACS_XACML_COMPARISON_INTEGER_GRT: _('Integer 1 greater than integer 2'), + ACS_XACML_COMPARISON_INTEGER_GRT_OE: _('Integer 1 greater than or equal integer 2'), + ACS_XACML_COMPARISON_INTEGER_LT: _('Integer 1 less than integer 2'), + ACS_XACML_COMPARISON_INTEGER_LT_OE: _('Integer 1 less than or equal integer 2'), + ACS_XACML_COMPARISON_DOUBLE_GRT: _('Double 1 greater than double 2'), + ACS_XACML_COMPARISON_DOUBLE_GRT_OE: _('Double 1 greater than or equal double 2'), + ACS_XACML_COMPARISON_DOUBLE_LT: _('Double 1 less than double 2'), + ACS_XACML_COMPARISON_DOUBLE_LT_OE: _('Double 1 less than or equal double 2'), +} + +XACML_COMPARISON_TYPE_DIC = dict(XACML_COMPARISON_EQUALITY_TYPE_DIC.items() \ + + XACML_COMPARISON_DIFF_TYPE_DIC.items()) + +XACML_COMPARISON_TYPE = XACML_COMPARISON_EQUALITY_TYPE + XACML_COMPARISON_DIFF_TYPE + +ACS_COMP_TYPE = { + ACS_XACML_COMPARISON_INTEGER_GRT: ACS_XACML_DATATYPE_INTEGER, + ACS_XACML_COMPARISON_INTEGER_GRT_OE: ACS_XACML_DATATYPE_INTEGER, + ACS_XACML_COMPARISON_INTEGER_LT: ACS_XACML_DATATYPE_INTEGER, + ACS_XACML_COMPARISON_INTEGER_LT_OE: ACS_XACML_DATATYPE_INTEGER, + ACS_XACML_COMPARISON_DOUBLE_GRT: ACS_XACML_DATATYPE_DOUBLE, + ACS_XACML_COMPARISON_DOUBLE_GRT_OE: ACS_XACML_DATATYPE_DOUBLE, + ACS_XACML_COMPARISON_DOUBLE_LT: ACS_XACML_DATATYPE_DOUBLE, + ACS_XACML_COMPARISON_DOUBLE_LT_OE: ACS_XACML_DATATYPE_DOUBLE, + ACS_XACML_COMPARISON_EQUALITY_STRING: ACS_XACML_DATATYPE_STRING, + ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE: ACS_XACML_DATATYPE_STRING, + ACS_XACML_COMPARISON_EQUALITY_BOOLEAN: ACS_XACML_DATATYPE_BOOLEAN, + ACS_XACML_COMPARISON_EQUALITY_INTEGER: ACS_XACML_DATATYPE_INTEGER, + ACS_XACML_COMPARISON_EQUALITY_DOUBLE: ACS_XACML_DATATYPE_DOUBLE, + ACS_XACML_COMPARISON_EQUALITY_DATE: ACS_XACML_DATATYPE_DATE, + ACS_XACML_COMPARISON_EQUALITY_TIME: ACS_XACML_DATATYPE_TIME, + ACS_XACML_COMPARISON_EQUALITY_DATETIME: ACS_XACML_DATATYPE_DATETIME, +# ACS_XACML_COMPARISON_EQUALITY_DAYTIMEDURATION: ACS_XACML_DATATYPE_DAYTIMEDURATION, +# ACS_XACML_COMPARISON_EQUALITY_YEARMONTHDURATION: ACS_XACML_DATATYPE_YEARMONTHDURATION, +# ACS_XACML_COMPARISON_EQUALITY_ANYURI: ACS_XACML_DATATYPE_ANYURI, +# ACS_XACML_COMPARISON_EQUALITY_X500NAME: ACS_XACML_DATATYPE_X500NAME, + ACS_XACML_COMPARISON_EQUALITY_RFC822NAME: ACS_XACML_DATATYPE_RFC822NAME, + ACS_XACML_COMPARISON_EQUALITY_IPADDRESS: ACS_XACML_DATATYPE_IPADDRESS, +# ACS_XACML_COMPARISON_EQUALITY_HEXBINARY: ACS_XACML_DATATYPE_HEXBINARY, +# ACS_XACML_COMPARISON_EQUALITY_BASE64BINARY: ACS_XACML_DATATYPE_BASE64BINARY, +}