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,
+}