670 lines
26 KiB
Python
670 lines
26 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 django.utils.translation import ugettext as _
|
|
|
|
from acs.models import Namespace, UserAlias
|
|
|
|
from acs.abac.models import *
|
|
|
|
from attribute_aggregator.xacml_constants import *
|
|
from attribute_aggregator.core import get_attribute_type_of_definition
|
|
|
|
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):
|
|
logger.debug("check_predicate: predicate id %s" % predicate.id)
|
|
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):
|
|
'''
|
|
find in profile the data corresponding to this definition
|
|
'''
|
|
for source in data1.sources.all():
|
|
l_data1 += profile.get_data_of_definition_and_source(\
|
|
data1.get_definition(), source)
|
|
else:
|
|
l_data1 = [data1.get_attribute_data()]
|
|
#operand2
|
|
l_data2 = []
|
|
data2 = predicate.operand2.get_assertion_instance()
|
|
if isinstance(data2, AssertionDefinition):
|
|
'''
|
|
find in profile the data corresponding to this definition
|
|
'''
|
|
for source in data2.sources.all():
|
|
l_data2 += profile.get_data_of_definition_and_source(\
|
|
data2.get_definition(), source)
|
|
else:
|
|
l_data2 = [data2.get_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):
|
|
logger.debug("compare_two_data: check if definitions match")
|
|
if data1.get_definition() != data2.get_definition():
|
|
logger.debug("compare_two_data: \
|
|
Definition %s does not match with %s" \
|
|
% (data1.get_definition(), data2.get_definition()))
|
|
return False
|
|
|
|
logger.debug("compare_two_data: convert values...")
|
|
data1_values = data1.get_converted_values()
|
|
data2_values = data2.get_converted_values()
|
|
|
|
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 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 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,
|
|
get_attribute_type_of_definition(data1.get_definition()),
|
|
predicate.comparison_type,
|
|
predicate.multivalues)
|
|
|
|
elif predicate.comparison_type in ACS_XACML_COMPARISON:
|
|
return test_diff_of_multivalues(data1_values, data2_values,
|
|
get_attribute_type_of_definition(data1.get_definition()),
|
|
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], data2_values[0]))
|
|
if test_diff_two_values(data1_values[0], data2_values[0],
|
|
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, v2, 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, v2, 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, v2, 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, v2, 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].lower() == data2_values[0].lower():
|
|
return True
|
|
elif data1_values[0] == data2_values[0]:
|
|
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.lower() == v2.lower():
|
|
return True
|
|
elif v1 == v2:
|
|
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.lower() == v2.lower():
|
|
found = True
|
|
elif v1 == v2:
|
|
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.lower() == v2.lower():
|
|
found = True
|
|
elif v1 == v2:
|
|
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)
|
|
if not predicate.single_value:
|
|
logger.debug("check_predicate_required: single value not required")
|
|
for source in predicate.get_sources():
|
|
d = profile.get_data_of_definition_and_source(\
|
|
predicate.get_definition(), source)
|
|
if d:
|
|
logger.debug("check_predicate_required: satisfied by %s" \
|
|
% str([a.__unicode__() for a in d]))
|
|
return True
|
|
else:
|
|
logger.debug("check_predicate_required: single value required")
|
|
found = False
|
|
match = [profile.get_data_of_definition_and_source(\
|
|
predicate.get_definition(), source) \
|
|
for source in predicate.get_sources()]
|
|
if len(match) == 1:
|
|
logger.debug("check_predicate_required: OK only one attribute \
|
|
with a single value in profile")
|
|
return True
|
|
else:
|
|
logger.debug("check_predicate_required: Failed - no value found \
|
|
or multiple")
|
|
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
|
|
|
|
|
|
@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
|
|
|
|
Objects to delete for predicate role:
|
|
- None
|
|
|
|
Objects to delete for predicate comparisons:
|
|
- AssertionDefinition
|
|
- AssertionData
|
|
'''
|
|
|
|
instance = predicate.get_predicate_instance()
|
|
if isinstance(instance, PredicateRole):
|
|
pass
|
|
elif isinstance(instance, PredicateRequired):
|
|
logger.debug('remove_predicate: predicate required found')
|
|
instance.assertion_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')
|
|
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')
|
|
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')
|
|
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')
|
|
logger.debug('remove_predicate: \
|
|
remove assertion data with id %s' % assertion.id)
|
|
assertion.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')
|
|
|
|
|
|
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 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
|