oasis-tcs / xacml-spec

OASIS XACML TC: List for tracking issues and features for the OASIS XACML TC. https://github.com/oasis-tcs/xacml-spec
Other
4 stars 0 forks source link

Enable policy writers to define and reuse composite functions in order to simplify Apply expressions across policies #34

Open cdanger opened 1 month ago

cdanger commented 1 month ago

22 and #23 showed the need to add new XACML functions in order to simplify very common Apply expressions that compose (math sense) two or more existing functions together.

For example, in #23 , the proposed empty-bag (resp. non-empty-bag) function is a composition of bag-size andinteger-equal(resp. integer-greater-than) functions.

So instead of adding such functions to the XACML spec every time we need a new one, they could be defined by the policy writers themselves in XACML at a global level (like #12 ?) so that they can be reused in Apply expressions, furthermore shared with other policy writers, and even further with the whole XACML community, on the TC's github for instance, as community extensions to the standard.

So we need a syntax to define new functions based on function composition, i.e. calling other functions:

<xs:element name="FunctionDefinition" type="FunctionDefinitionType">
  <!--  A VariableReference used in a Function's definition must refer to one of the function's variables. -->
  <xs:key name="FunctionVariableKey"  >  
    <xs:selector xpath="Variable" />  
    <xs:field xpath="@Id" />  
  </xs:key>  
  <xs:keyref name="FunctionVariableKeyRef" refer="FunctionVariableKey">  
    <xs:selector xpath="//VariableReference" />  
    <xs:field xpath="@VariableId" />  
  </xs:keyref>  
</xs:element>

<xs:complexType name="FunctionDefinitionType">
  <xs:complexContent>
      <xs:sequence>
        <!-- Function variables-->
        <xs:element name="Variable" type="xs:ID" minOccurs="1" maxOccurs="unbounded" />
        <!-- Function expression, i.e. call to another function with certain arguments -->
        <xs:element ref="xacml:ApplyExpression"/>
      </xs:sequence>
      <xs:attribute name="FunctionId" type="xs:anyURI" use="required"/>
  </xs:complexContent>
</xs:complexType>

<xs:element name="ApplyExpression" type="ApplyExpressionType" />
<!-- Similar to Apply but as it is in a Function Definition that must be context-independant, i.e. not depending on request context, AttributeDesignators/AttributeSelectors are not allowed as arguments here, i.e. only VariableReferences to the Function's VariablesAttributeValues, AttributeValues, <Function>s,  and ApplyExpressions (recursive)  -->
<xs:complexType name="ApplyExpressionType">
  <xs:complexContent>
    <xs:choice minOccurs="1" maxOccurs="unbounded">
          <!-- Reference to one of the Function Variables (defined in the FunctionDefinition)-->
          <xs:element ref="xacml:VariableReference"/>
          <xs:element ref="xacml:AttributeValue"/>
          <xs:element ref="xacml:Function"/>
          <xs:element ref="xacml:ApplyExpression"/>
     </xs:choice>
    <xs:attribute name="FunctionId" type="xs:anyURI" use="required"/>
  </xs:complexContent>
</xs:complexType>

Some examples based on #22 and #23 (defining empty-bag, not-empty-bag, integer-average functions):

<FunctionDefinition FunctionId="urn:oasis:names:tc:xacml:3.0:function:empty-bag">
  <Variable>bag</Variable>
  <ApplyExpression FunctionId="urn:oasis:names:tc:xacml:1.0:function:integer-equal">
    <ApplyExpression FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-bag-size">
      <VariableReference VariableId="bag" />
    </ApplyExpression>
    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#integer">0</AttributeValue>
  </ApplyExpression>
</FunctionDefinition>

<FunctionDefinition FunctionId="urn:oasis:names:tc:xacml:3.0:function:not-empty-bag">
  <Variable>bag</Variable>
  <ApplyExpression FunctionId="urn:oasis:names:tc:xacml:1.0:function:integer-greater-than">
    <ApplyExpression FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-bag-size">
      <VariableReference VariableId="bag" />
    </ApplyExpression>
    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#integer">0</AttributeValue>
  </ApplyExpression>
</FunctionDefinition>

Now you can do:


<Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:empty-bag">
  <AttributeDesignator
              Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
              AttributeId="http://example.com/some-attribute"
              DataType="http://www.w3.org/2001/XMLSchema#string"  MustBePresent="false"/>
 </Apply>

 <Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:non-empty-bag">
  <AttributeDesignator
              Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
              AttributeId="http://example.com/some-attribute"
              DataType="http://www.w3.org/2001/XMLSchema#string"  MustBePresent="false"/>
</Apply>

TODO:

  1. Define a standard package format or parent XML element for wrapping such <FunctionDefinition>s, maybe the same as GlobalVariableDefinitions (#12).
steven-legg commented 1 month ago

What you are talking about is equivalent to a global/shared variable (#12) with parameters. A function with no parameters is the same as a variable definition. A common mechanism could be defined.

steven-legg commented 1 month ago

I can think of a roundabout way to calculate minimum or maximum with existing functions but not a way to calculate sum or average.

cdanger commented 1 month ago

I can think of a roundabout way to calculate minimum or maximum with existing functions but not a way to calculate sum or average.

Indeed, I don't see how to do sum either with existing stuff. But having sum in the standard would be enough to express the average since it is based on the sum (divided by size), see the example above.

EDIT: actually, I've removed the example for average just now, because I am thinking of a different approach: if we add a new higher-order function like the XPath array:fold-left (but for bags) to the standard, we could define sum, average, min, max by composing the fold-left function with existing functions; except for the integer-min function it's a bit tricky to pick the initial value as fold-left's 2nd argument, since it must be bigger than all the bag values for the min comparison to work but there is now absolute max value for xs:integer in XML schema. So integer-min is probably not a good example for this :-( .

cdanger commented 1 month ago

What you are talking about is equivalent to a global/shared variable (#12) with parameters. A function with no parameters is the same as a variable definition. A common mechanism could be defined.

Yes, sure. GlobalVariableDefinitions and FunctionDefinitions can be put in the same package.

steven-legg commented 1 month ago

Yes, sure. GlobalVariableDefinitions and FunctionDefinitions can be put in the same package.

The synergy disappears if you are going to exclude attribute designators and selectors from function definitions. My PAPs support something that looks like global variables and I have a non-trivial policy collection where every single global variable definition depends on at least one attribute designator. The usefulness of parameterized global variables would be severely limited if they couldn't also use attribute designators.

The roundabout way I thought of to calculate minimum or maximum depends on the Select expression from the Entities profile, which isn't part of your function definition. However, Select and the other quantified expressions have embedded Expression elements and so would need new rewrites for use in function definitions in order to exclude designators and selectors.

In any case, I can bypass the prohibition on attribute designator and selector elements by using the attribute-designator and attribute-selector functions from the Entities profile.

steven-legg commented 1 month ago

@cdanger What is the rationale for excluding attribute designators and selectors? Is it because they would be hidden inputs to the function? Perhaps a different name is in order, e.g., method, because designators would be very useful. A policy writer can always choose not to use designators.

cdanger commented 3 weeks ago

Because I have this hard habit to use the term "function" only for pure functions, which have nice properties that functional programmers would expect (output depends only on input, no side effects). As I had the impression that all functions in the XACML core spec were pure functions, I assumed this was implicit. But checking again... I was wrong :-( . At least access-permitted (which I don't use) is impure, and so are the attribute-designator and attribute-selector (new to me) that you mentioned earlier. Sorry for the confusion. Maybe I should rename it to PureFunctionDefinition...

Anyway, if you are just looking for reusable expressions with attribute designators and selectors, it seems to me you can do it with GlobalVariableDefinitions already, and you don't need all this.

steven-legg commented 3 weeks ago

Anyway, if you are just looking for reusable expressions with attribute designators and selectors, it seems to me you can do it with GlobalVariableDefinitions already, and you don't need all this.

The current global variable proposal doesn't include parameterization, but it would be a useful addition. Parameterized global variable definitions would be a superset of pure, composite functions making pure, composite functions redundant complexity.

cdanger commented 3 weeks ago

Not the same guarantees though! (Due to the constraint of not depending on attributes / external variables internally - only input ones - or any external state). If I want to call a function / expression with the guarantees of determinism / predictability, no side effect, referential transparency (I can cache the result), immutability... then I want a pure function, not any variable expression. Also pure functions can be composed together (guarantees preserved), i.e. a composition of pure functions is a pure function.