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/abac/core.py

1046 lines
43 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/>.
'''
import re
import logging
import datetime
import time
from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from acs.abac.models import *
from acs.xacml.constants import *
from acs.core import get_alias_in_policy_from_namespace, \
stack_of_roles_from_user
logger = logging.getLogger('abac')
def extract_predicate_ids(expression):
if not expression:
return []
ids = []
res = re.search(r'\d+', expression)
while res and res.group(0):
ids.append(res.group(0))
p = r'(%s)' % res.group(0)
expression = re.sub(p, '', expression)
res = re.search(r'\d+', expression)
return ids
def check_predicate(predicate, profile):
if isinstance(predicate, PredicateRequired):
logger.debug("check_predicate: PredicateRequired %s" % predicate)
return check_predicate_required(predicate, profile)
if isinstance(predicate, PredicateComparison):
logger.debug("check_predicate: PredicateComparison %s" % predicate)
return check_predicate_comparison(predicate, profile)
if isinstance(predicate, PredicateRole):
logger.debug("check_predicate: PredicateRole %s" % predicate)
return check_predicate_role(predicate, profile)
return False
'''
Every assertionData in the profile with a definition and a list of
sources are candidates for redicate checking.
This match assertions provided by multiple source or even multiple
from the same source.
!!! One of them is enough to satisfy the comparison. !!!
In other words the multivalue management is handled only for
multivalues of the assertion, not between multiple assertions
See function below to see how multi valued attributes are treated
'''
def check_predicate_comparison(predicate, profile):
#operand1
l_data1 = []
data1 = predicate.operand1.get_assertion_instance()
if isinstance(data1, AssertionDefinition):
sources = AttachedSource.objects.filter(assertion=data1)
if not sources:
return False
definition = data1.attribute_definition
for source in sources:
for ad in profile.assertions.all():
if ad.source.id == source.source.id \
and ad.attribute_data.definition.id == definition.id:
l_data1.append(ad.attribute_data)
if not l_data1:
return False
else:
l_data1.append(data1.attribute_data)
#operand2
l_data2 = []
data2 = predicate.operand2.get_assertion_instance()
if isinstance(data2, AssertionDefinition):
sources = AttachedSource.objects.filter(assertion=data2)
if not sources:
return False
definition = data2.attribute_definition
for source in sources:
for ad in profile.assertions.all():
if ad.source.id == source.source.id \
and ad.attribute_data.definition.id == definition.id:
l_data2.append(ad.attribute_data)
if not l_data2:
return False
else:
l_data2.append(data2.attribute_data)
for d1 in l_data1:
for d2 in l_data2:
if compare_two_data(predicate, d1, d2):
return True
return False
'''
A predicate contains an option indicating if an attribute must be single valued.
Since considered has an enforcement, a check includes the test that attribute has a single value if required,
else that a multivalue option not NO_MULTIVALUES must be set.
All:
NO_MULTIVALUES
Both operand are single valued attributes
Equality:
EQUAL_ONE_VALUE
At least one value of the values of OP1 is equal to one value of the values of OP2
EQUAL_OP1_SUBSET_OP2
The values of OP1 is is a subset of the values of OP2
EQUAL_EXACT_MATCH
Equal set of values
Diff strict:
DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT
All values of OP1 must be less than the highest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT
All values of OP1 must be greater than the highest value of OP2
DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT
All values of OP1 must be less than the smallest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT
All values of OP1 must be greater than the smallest value of OP2
DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT
At least one value of OP1 must be less than the highest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT
At least one value of OP1 must be greater than the highest value of OP2
DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT
At least one value of OP1 must be less than the smallest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT
At least one value of OP1 must be greater than the smallest value of OP2
Diff or equal:
Same as for strict and equality is treated as follows:
DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT_OE
All values of OP1 must be less than or equal to the highest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT_OE
All values of OP1 must be greater than or equal to the highest value of OP2
DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT_OE
All values of OP1 must be less than or equal to the smallest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT_OE
All values of OP1 must be greater than or equal to the smallest value of OP2
DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT_OE
At least one value of OP1 must be less than or equal to the highest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT_OE
At least one value of OP1 must be greater than or equal to the highest value of OP2
DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2
ACS_XACML_COMPARISON_INTEGER_LT_OE
At least one value of OP1 must be less than or equal to the smallest value of OP2
ACS_XACML_COMPARISON_INTEGER_GRT_OE
At least one value of OP1 must be greater than or equal to the smallest value of OP2
To deal with richer comparison and equality of multivalued attributes, a 'or' statement should be used
'''
def compare_two_data(predicate, data1, data2):
if data1.definition.id != data2.definition.id:
return False
if data1.definition.attribute_type == ACS_XACML_DATATYPE_STRING:
data1_values = StringM.objects.filter(data=data1)
data2_values = StringM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_BOOLEAN:
data1_values = BooleanM.objects.filter(data=data1)
data2_values = BooleanM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_INTEGER:
data1_values = IntegerM.objects.filter(data=data1)
data2_values = IntegerM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_DOUBLE:
data1_values = DoubleM.objects.filter(data=data1)
data2_values = DoubleM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_TIME:
data1_values = TimeM.objects.filter(data=data1)
data2_values = TimeM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_DATE:
data1_values = DateM.objects.filter(data=data1)
data2_values = DateM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_DATETIME:
data1_values = DateTimeM.objects.filter(data=data1)
data2_values = DateTimeM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_RFC822NAME:
data1_values = Rfc822NameM.objects.filter(data=data1)
data2_values = Rfc822NameM.objects.filter(data=data2)
elif data1.definition.attribute_type == ACS_XACML_DATATYPE_IPADDRESS:
data1_values = IpAddressM.objects.filter(data=data1)
data2_values = IpAddressM.objects.filter(data=data2)
if not data1_values or not data2_values:
logger.debug("compare_two_data: \
Return False because no values was found for predicate %s" \
% predicate)
return False
if predicate.operand1_single_value and len(data1_values) > 1:
logger.debug("compare_two_data: \
Return False because a single value is required for operand one \
and multiple were found %s for predicate %s" \
% ([x.value for x in data1_values], predicate))
return False
if predicate.operand2_single_value and len(data2_values) > 1:
logger.debug("compare_two_data: \
Return False because a single value is required for operand two \
and multiple were found %s for predicate %s" \
% ([x.value for x in data2_values], predicate))
return False
if not(predicate.operand1_single_value \
and predicate.operand2_single_value) \
and ((predicate.comparison_type in XACML_COMPARISON_EQUALITY \
and not predicate.multivalues in \
('EQUAL_ONE_VALUE',
'EQUAL_OP1_SUBSET_OP2',
'EQUAL_EXACT_MATCH')) \
or (predicate.comparison_type in ACS_XACML_COMPARISON \
and not predicate.multivalues in \
('DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2',
'DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2',
'DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2',
'DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2'))):
logger.debug("compare_two_data: \
Return False because multivalued attributes are accepted and no \
suitable management option has been selected for predicate %s" \
% str(predicate))
return False
logger.debug("compare_two_data: \
Evaluation of predicate %s" \
% str(predicate))
if predicate.comparison_type in XACML_COMPARISON_EQUALITY:
return test_equality_of_values(data1_values, data2_values,
data1.definition.attribute_type,
predicate.comparison_type,
predicate.multivalues)
elif predicate.comparison_type in ACS_XACML_COMPARISON:
return test_diff_of_multivalues(data1_values, data2_values,
data1.definition.attribute_type,
predicate.comparison_type,
predicate.multivalues)
logger.debug("compare_two_data: \
Return False because of unknown comparison type \
for predicate %s" \
% predicate)
return False
def test_diff_of_multivalues(data1_values, data2_values, data_type, comparison_type,
test_type='NO_MULTIVALUES'):
if test_type == 'NO_MULTIVALUES':
logger.debug("test_diff_of_multivalues: \
No multivalues, test %s and %s" \
% (data1_values[0].value, data2_values[0].value))
if test_diff_two_values(data1_values[0].value, data2_values[0].value, comparison_type):
logger.debug("test_diff_of_multivalues: True")
return True
else:
logger.debug("test_diff_of_multivalues: False")
return False
# BOTTOM LIMIT with LT and UPPER LIMIT with GTR
# OP1a <= OP1b <= OP2a <= OP2b
# OP1b >= OP1a >= OP2b >= OP2a
# With ALL and LT, no value of OP1 must be superior to the bottom limit of OP2
# With ALL and GRT, no value of OP1 must be less than the upper limit of OP2
elif (test_type == 'DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_LT + \
ACS_XACML_COMPARISON_LT_OE) \
or (test_type == 'DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_GRT + \
ACS_XACML_COMPARISON_GRT_OE):
for v1 in data1_values:
for v2 in data2_values:
if not test_diff_two_values(v1.value, v2.value, comparison_type):
logger.debug("test_diff_of_multivalues: \
DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2 (LT) or \
DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2 (GRT) - \
predicate not satisfied")
return False
return True
# UPPER LIMIT with LT and BOTTOM LIMIT with GTR
# OP2a <= OP1a <= OP1b <= OP2b
# OP2b >= OP1b >= OP2a >= OP2a
# With ALL and LT, no value of OP1 must be superior to the upper limit of OP2
# With ALL and GRT, no value of OP1 must be less than the bottom limit of OP2
elif (test_type == 'DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_GRT + \
ACS_XACML_COMPARISON_GRT_OE) \
or (test_type == 'DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_LT + \
ACS_XACML_COMPARISON_LT_OE):
for v2 in data2_values:
found = True
for v1 in data1_values:
if not test_diff_two_values(v1.value, v2.value, comparison_type):
found = False
if found:
return True
logger.debug("test_diff_of_multivalues: \
DIFF_ALL_OP1_WITH_BOTTOM_LIMIT_OP2 (GRT) or \
DIFF_ALL_OP1_WITH_UPPER_LIMIT_OP2 (LT) - \
predicate not satisfied")
return False
# BOTTOM LIMIT with LT and UPPER LIMIT with GTR
# OP1a <= OP2a <= OP1b <= OP2b
# OP1b >= OP2b >= OP2a >= OP2a
# With ONE and LT, some values of OP1 may be superior to the bottom limit of OP2
# With ONE and GRT, some values of OP1 must be less than the upper limit of OP2
elif (test_type == 'DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_LT + \
ACS_XACML_COMPARISON_LT_OE) \
or (test_type == 'DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_GRT + \
ACS_XACML_COMPARISON_GRT_OE):
for v1 in data1_values:
found = True
for v2 in data2_values:
if not test_diff_two_values(v1.value, v2.value, comparison_type):
found = False
if found:
return True
logger.debug("test_diff_of_multivalues: \
DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2 (LT) or \
DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2 (GRT) - \
predicate not satisfied")
return False
# UPPER LIMIT with LT and BOTTOM LIMIT with GTR
# OP1a <= OP2a <= OP1b <= OP2b
# OP1b >= OP2b >= OP2a >= OP2a
# With ONE and LT, some values of OP1 may be superior to the upper limit of OP2
# With ONE and GRT, some values of OP1 must be less than the bottom limit of OP2
elif (test_type == 'DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_LT + \
ACS_XACML_COMPARISON_LT_OE) \
or (test_type == 'DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2' \
and comparison_type in ACS_XACML_COMPARISON_GRT + \
ACS_XACML_COMPARISON_GRT_OE):
for v1 in data1_values:
for v2 in data2_values:
if test_diff_two_values(v1.value, v2.value, comparison_type):
return True
logger.debug("test_diff_of_multivalues: \
DIFF_ONE_OP1_WITH_BOTTOM_LIMIT_OP2 (GRT) or \
DIFF_ONE_OP1_WITH_UPPER_LIMIT_OP2 (LT) - \
predicate not satisfied")
return False
logger.debug("test_diff_of_multivalues: \
Unknown multivalue option, \
predicate not satisfied")
return False
def test_diff_two_values(value1, value2, comparison_type):
if comparison_type in ACS_XACML_COMPARISON_GRT:
if value1 > value2:
return True
elif comparison_type in ACS_XACML_COMPARISON_GRT_OE:
if value1 >= value2:
return True
elif comparison_type in ACS_XACML_COMPARISON_LT:
if value1 < value2:
return True
elif comparison_type in ACS_XACML_COMPARISON_LT_OE:
if value1 <= value2:
return True
return False
def test_equality_of_values(data1_values, data2_values, data_type, comparison_type,
test_type='NO_MULTIVALUES'):
if test_type == 'NO_MULTIVALUES':
if data_type == ACS_XACML_DATATYPE_STRING \
and comparison_type == \
ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE:
if data1_values[0].value.lower() == data2_values[0].value.lower():
return True
elif data1_values[0].value == data2_values[0].value:
return True
elif test_type == 'EQUAL_ONE_VALUE':
for v1 in data1_values:
for v2 in data2_values:
if data_type == ACS_XACML_DATATYPE_STRING \
and comparison_type == \
ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE:
if v1.value.lower() == v2.value.lower():
return True
elif v1.value == v2.value:
return True
elif test_type == 'EQUAL_OP1_SUBSET_OP2':
for v1 in data1_values:
found = False
for v2 in data2_values:
if data_type == ACS_XACML_DATATYPE_STRING \
and comparison_type == \
ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE:
if v1.value.lower() == v2.value.lower():
found = True
elif v1.value == v2.value:
found = True
if not found:
return False
return True
elif test_type == 'EQUAL_EXACT_MATCH':
if len(data1_values) != len(data2_values):
return False
for v1 in data1_values:
found = False
for v2 in data2_values:
if data_type == ACS_XACML_DATATYPE_STRING \
and comparison_type == \
ACS_XACML_COMPARISON_EQUALITY_STRING_IGN_CASE:
if v1.value.lower() == v2.value.lower():
found = True
elif v1.value == v2.value:
found = True
if not found:
return False
return True
return False
def check_predicate_required(predicate, profile):
'''
Look in profile all attributedata with that attribute type
Then check the source and return True on the first acceptable source
A single value attribute is checked per sources
Declaring multiple source is a OR between sources.
So the single-valued is checked per source.
If it is expected to check a unique value with multiple sources
it is required to use multiple required predicates and NOT operators
'''
logger.debug("check_predicate_required: check %s" % predicate)
definition = predicate.definition.attribute_definition
sources = AttachedSource.objects.filter(assertion=predicate.definition)
if not predicate.single_value:
logger.debug("check_predicate_required: single value not required")
for source in sources:
for ad in profile.assertions.all():
if ad.source.id == source.source.id \
and ad.attribute_data.definition.id == definition.id:
logger.debug("check_predicate_required: satisfied")
return True
else:
logger.debug("check_predicate_required: single value required")
found = False
for source in sources:
c = 0
for ad in profile.assertions.all():
if ad.source.id == source.source.id \
and ad.attribute_data.definition.id == definition.id:
c = c + 1
if c > 1:
logger.debug("check_predicate_required: multiple values found \
- not satisfied")
return False
elif c == 1:
found = True
if found:
return True
logger.debug("check_predicate_required: At least one source with a \
unique value found")
return False
def check_predicate_role(predicate, profile):
'''
Check that the user has the role or a senior role.
The user must be in the profile.
'''
if not predicate or not profile \
or not isinstance(predicate, PredicateRole) \
or not predicate.role \
or not profile.user:
return False
alias = get_alias_in_policy_from_namespace(profile.user, predicate.role.namespace)
if not alias:
logger.debug("check_predicate_role: no alias found for user: %s \
in namespace %s" % (profile.user, predicate.role.namespace))
return False
logger.debug("check_predicate_role: check if user %s has role %s" \
% (alias, predicate.role))
stack = stack_of_roles_from_user(alias)
logger.debug("check_predicate_role: roles of the user: %s" \
% stack)
if predicate.role in stack:
logger.debug("check_predicate_role: success")
return True
logger.debug("check_predicate_role: failure")
return False
def check_predicates(rule, profile):
'''
Parse rule and list predicates
Check presence, source, values...
'''
if not rule or not profile or not rule.expression:
return {}
predicates = []
dic = {}
for p in Predicate.objects.filter(rule=rule):
p_t = p.get_predicate_instance()
predicates.append(p_t)
dic[str(p.id)] = check_predicate(p_t, profile)
logger.debug("check_predicates: found %s" \
% [str(x) for x in predicates])
logger.debug("check_predicates: computed %s" % dic)
return dic
def get_source_form_name(name):
try:
return Source.objects.get(name=name)
except:
return None
def get_def_from_name_and_ns(name, namespace):
if not name or not namespace:
return None
try:
ans = AttributeName.objects.filter(name=name)
for an in ans:
if an.attribute_map.namespace.identifier == namespace:
return an.attribute_map.attribute_definition
return None
except:
return None
#@transaction.commit_manually
def add_assertion_to_profile(profile, source, definition, values,
expiration_date=None):
'''
Values is a list of strings converted to the data type given by the
definition datatype
'''
a = None
logger.debug('add_assertion_to_profile: Add assertion to profile %s \
- (%s, %s, %s)' % (profile, source, definition, str(values)))
try:
data = None
try:
data = AttributeData(definition=definition)
data.save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to add the assertion \
data due to %s' % err)
raise err
if definition.attribute_type == ACS_XACML_DATATYPE_STRING:
for value in values:
try:
StringM(data=data, value=value).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to add value \
%s due to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_BOOLEAN:
for value in values:
try:
v = False
if value in ('True', 'true', 'Vrai', 'vrai'):
v = True
BooleanM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
boolean or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_INTEGER:
for value in values:
try:
v = int(value)
IntegerM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
integer or add value %s due to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_DOUBLE:
for value in values:
try:
v = float(value)
IntegerM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
double or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_TIME:
for value in values:
try:
v = time.strptime(value,"%h:%m:%s") #12:15:00
TimeM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
time or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_DATE:
for value in values:
try:
v = time.strptime(value,"%d/%b/%Y") #28/01/1982
DateM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
date or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_DATETIME:
for value in values:
try:
v = time.strptime(value,"%d/%b/%Y-%h:%m:%s") #28/01/1982-03:00:00
DateTimeM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
datetime or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_RFC822NAME:
for value in values:
try:
v = None
r = re.compile('[a-zA-Z0-9+_\-\.]+@[0-9a-zA-Z][.-0-9a-zA-Z]*.[a-zA-Z]+') # email
if r.search(value):
v = value
else:
logger.error('add_assertion_to_profile: Unable to convert in \
rfc822name or add value %sdue to %s' % (value, err))
Rfc822NameM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
rfc822name or add value %sdue to %s' % (value, err))
elif definition.attribute_type == ACS_XACML_DATATYPE_IPADDRESS:
for value in values:
try:
v = None
r = re.compile('(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})') # x.x.x.x
if r.search(value):
v = value
else:
logger.error('add_assertion_to_profile: Unable to convert in \
ipaddress or add value %sdue to %s' % (value, err))
IpAddressM(data=data, value=v).save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to convert in \
ipaddress or add value %sdue to %s' % (value, err))
else:
logger.error('add_assertion_to_profile: Unknown definition data type \
%s' % definition.attribute_type)
raise Exception()
if expiration_date:
#expiration_date = time.strptime(expiration_date,"%d/%b/%Y-%h:%m:%s") #28/01/1982-03:00:00
logger.debug('add_assertion_to_profile: expiration date %s found \
- conversion' % expiration_date)
try:
expiration_date = iso8601_to_datetime(expiration_date)
except:
logger.error('add_assertion_to_profile: Conversion failed - \
the date is not ISO8601')
expiration_date = None
'''
Possibilities if the profile contains an assertion with the same
definition and the same source:
- append values if expiration_date are the same
- remove and the new assertion
- add a new assertion withtou checking
The third choices is made.
Then the profile may contain multiple assertion for a same
definition and source.
!!! It is enough that one of them satisfies the predicate. !!!
cf. functions check_predicate_comparison and
check_predicate_required
If it is not the behaviour expected, reduce expiration date.
'''
try:
a = AssertionData(source=source, attribute_data=data,
expiration_date=expiration_date)
a.save()
profile.assertions.add(a)
profile.save()
except Exception, err:
logger.error('add_assertion_to_profile: Unable to add the assertion \
data due to %s' % err)
raise err
except Exception, err:
# transaction.rollback()
return None
else:
# transaction.commit()
logger.debug('add_assertion_to_profile: everything successfully \
performed- %s added' % a)
return a
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)
logger.debug('add_assertion_to_profile: m %s' % str(m))
if not m:
raise ValueError('Invalid ISO8601 date')
tm = time.strptime(m.group(1)+'Z', "%Y-%m-%dT%H:%M:%SZ")
logger.debug('add_assertion_to_profile: tm %s' % str(tm))
return datetime.datetime.fromtimestamp(time.mktime(tm))
def arrange_missing_predicates(missing_predicates, profile):
'''
Here we have to retreat the priority of possibilities to satisfy
the rule.
missing_predicates present mutliples combinaisons possible to satisfy
the rule.
Output: we want to produce a rule with all the possibilities sorted by
the easiest to the hardest: rule = easiest_rule | ... | hardest_rule
This sort is based on the number of predicate not statisfied but also
on the following rules:
- First predicates required
- Comparison if predicate is False because an operande is missing
- Comparisons with only def
- comparison with data not satisfied
In practice, we prefer to ask the user to present its surname and its
firstname (2 predicate False up to now) rather to ask to present its
age if it has already been presented but did not satisfied a
comparison (only one predicate False but harder to satisfy)
TODO
'''
pass
def make_new_rule_from_missing_predicates(missing_predicates):
if not missing_predicates:
return None
s = ''
for it in missing_predicates:
for r in missing_predicates[it]:
'Or'
s += '('
s_tmp = ''
for p_id, now, exp in r:
'And'
'If True to False put negative sign -'
if now:
s_tmp += '(-'
s_tmp += p_id
if now:
s_tmp += ')'
s_tmp += '&'
s += s_tmp[:-1]
s += ')'
if r:
s += '|'
if s:
return s[:-1]
else:
return None
def get_namespace_by_friendly_name(name):
if not name:
return None
try:
return AttributeNamespace.objects.get(friendly_name=name)
except ObjectDoesNotExist:
return None
except MultipleObjectsReturned:
print '###ERROR: multiple namespace with the friendly name %s' %ns
def get_namespace_by_identifier(identifier):
if not identifier:
return None
try:
return AttributeNamespace.objects.get(identifier=identifier)
except ObjectDoesNotExist:
return None
except MultipleObjectsReturned:
print '###ERROR: multiple namespace with the identifier name %s' %identifier
def get_attribute_definition_by_name(name):
if not name:
return None
try:
return AttributeDefinition.objects.get(attribute_name=name)
except ObjectDoesNotExist:
return None
except MultipleObjectsReturned:
print '###ERROR: multiple attribute definition with the name %s' %name
def get_all_attribute_definitions():
try:
return AttributeDefinition.objects.all()
except:
return None
def get_all_sources():
try:
return Source.objects.all()
except:
return None
@transaction.commit_manually
def load_or_create_user_profile(user, 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
'''
profile = None
try:
if user:
try:
logger.info('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.info('load_or_create_user_profile: Existing user \
profile %s for %s - No cleanup' % (profile, user))
else:
for ad in profile.assertions.all():
if not ad.expiration_date \
or ad.expiration_date < datetime.datetime.now():
logger.debug('load_or_create_user_profile: \
Remove outdated assertion %s of profile %s' \
% (ad, profile))
ad.delete()
else:
logger.debug('load_or_create_user_profile: \
%s kept in profile' \
% ad)
elif not user:
profile = UserAttributeProfile()
profile.save()
logger.info('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 load_profile_by_dic(profile, dic):
if not profile or not dic:
logger.error('load_profile_by_dic: \
Missing profile or dictionnary')
return
for source in dic:
logger.debug('load_profile_by_dic: loading from source with name: %s' \
% source)
s = get_source_form_name(source)
if s:
logger.debug('load_profile_by_dic: attributes: %s' \
% str(dic[source]))
for attr in dic[source]:
logger.debug('load_profile_by_dic: attribute %s of %s with values %s' \
% (attr['name'], attr['namespace'], str([x for x in attr['values']])))
expiration_date = None
if 'expiration_date' in attr:
logger.debug('load_profile_by_dic: expire at %s' \
% attr['expiration_date'])
expiration_date = attr['expiration_date']
d = get_def_from_name_and_ns(attr['name'], attr['namespace'])
if not d:
logger.error('load_profile_by_dic: \
definition not found for %s %s' \
% (attr['name'], attr['namespace']))
else:
logger.debug('load_profile_by_dic: \
definition %s found' % d)
a = add_assertion_to_profile(profile, s, d, attr['values'],
expiration_date=expiration_date)
if not a:
logger.debug('load_profile_by_dic: \
error adding assertion')
else:
logger.debug('load_profile_by_dic: \
assertion %s added' % a)
else:
logger.critical('load_profile_by_dic: \
The source with name %s and attributes %s is unknown of the system'
% (str(source), str(dic[source])))
@transaction.commit_manually
def remove_predicate(predicate):
try:
if not predicate:
raise Exception(_('No predicate provided'))
else:
logger.debug('remove_predicate: Begin deletion of predicate %s with id %s' % (predicate, predicate.id))
'''
Objects to delete for predicate required:
- AssertionDefinition
- Attached source
Objects to delete for predicate comparisons:
- AssertionDefinition
- Attached source
- AssertionData
- Attribute data
- Values
'''
instance = predicate.get_predicate_instance()
if isinstance(instance, PredicateRequired):
logger.debug('remove_predicate: predicate required found')
for s in AttachedSource.objects.filter(assertion=instance.definition):
logger.debug('remove_predicate: remove attached source with id %s' %s.id)
s.delete()
logger.debug('remove_predicate: remove assertion definition with id %s' %instance.definition.id)
instance.definition.delete()
elif isinstance(instance, PredicateComparison):
logger.debug('remove_predicate: predicate comparison found')
assertion = instance.operand1.get_assertion_instance()
if isinstance(assertion, AssertionDefinition):
logger.debug('remove_predicate: operand one is an assertion definition')
for s in AttachedSource.objects.filter(assertion=assertion):
logger.debug('remove_predicate: remove attached source with id %s' %s.id)
s.delete()
logger.debug('remove_predicate: remove assertion definition with id %s' %assertion.id)
assertion.delete()
elif isinstance(assertion, AssertionData):
logger.debug('remove_predicate: operand one is an assertion data')
for v in assertion.get_values():
logger.debug('remove_predicate: remove value %s with id %s' % (v, v.id))
v.delete()
logger.debug('remove_predicate: remove attribute data with id %s' % assertion.attribute_data.id)
assertion.attribute_data.delete()
logger.debug('remove_predicate: remove assertion data with id %s' % assertion.id)
assertion.delete()
else:
raise Exception(_('Unknown operand one'))
assertion = instance.operand2.get_assertion_instance()
if isinstance(assertion, AssertionDefinition):
logger.debug('remove_predicate: operand two is an assertion definition')
for s in AttachedSource.objects.filter(assertion=assertion):
logger.debug('remove_predicate: remove attached source with id %s' %s.id)
s.delete()
logger.debug('remove_predicate: remove assertion definition with id %s' %assertion.id)
assertion.delete()
elif isinstance(assertion, AssertionData):
logger.debug('remove_predicate: operand two is an assertion data')
for v in assertion.get_values():
logger.debug('remove_predicate: remove value %s with id %s' % (v, v.id))
v.delete()
data = assertion.attribute_data
logger.debug('remove_predicate: remove assertion data with id %s' % assertion.id)
assertion.delete()
logger.debug('remove_predicate: remove attribute data with id %s' % data.id)
data.delete()
else:
raise Exception(_('Unknown operand two'))
else:
raise Exception(_('Unknown predicate type'))
logger.debug('remove_predicate: deletion of the predicate')
predicate.delete()
except Exception, err:
transaction.rollback()
logger.critical('remove_predicate: error deleting predicate due to %s'
% err)
raise err
else:
transaction.commit()
logger.debug('remove_predicate: predicate deleted')
@transaction.commit_manually
def remove_rule(rule):
try:
if not rule:
raise Exception(_('No rule provided'))
else:
logger.debug('remove_rule: Begin deletion of rule %s with id %s' % (rule, rule.id))
for p in Predicate.objects.filter(rule=rule):
logger.debug('remove_rule: found predicate %s' % p)
remove_predicate(p)
logger.debug('remove_rule: deletion of the rule')
rule.delete()
except Exception, err:
transaction.rollback()
logger.critical('remove_rule: error deleting rule due to %s'
% err)
raise err
else:
transaction.commit()
logger.debug('remove_rule: rule deleted')