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

Parameterized Policies and Policy references (allowing to pass Variables to referenced policies) [UPDATES #9] #4

Open cdanger opened 1 year ago

cdanger commented 1 year ago

This is an enhancement of the feature described in issue #9 where we add a safe mechanism to extend the scope of VariableDefinitions to the referenced policies in a PolicySet.

Proposal (inspired by Functions): to extend somehow the scope of a PolicySet's VariableDefinitions to referenced policies, you'll be able to pass the Variables (values) explicitly to the referenced Policy(Set)s in arguments (like it is done for Functions in Apply elements), with one of the two following options:

  1. Option breaking backward compatibility: change the type PolicyIdReference (resp. PolicySetIdReference) to contain a sequence of zero or more Expression elements (<xs:element ref="xacml:Expression" minOccurs="0" maxOccurs="unbounded"/>) which are the optional arguments passed to the referenced Policy (resp. PolicySet).
  2. Backward-compatible option : add new type ParameterizedPolicyIdReference (resp. ParameterizedPolicySetIdReference) which extends PolicyIdReference (resp. PolicySetIdReference) and contains a sequence of one or more Expression elements (<xs:element ref="xacml:Expression" maxOccurs="unbounded"/>) which are the arguments passed to the referenced Policy (resp. PolicySet).

Option 2, as opposed to option 1, keeps the change to XACML schema backward-compatible, therefore suitable for the next minor release of XACML. However, it adds more complexity to the XACML schema on the long term (adding 2 new XML types) as compared to option 1 which enhances existing types.

In both options, each argument (Expression) passed to the referenced Policy/PolicySet must match a corresponding InputParameter declaration in that Policy(Set). This means that Policy/PolicySet type definitions are also changed to have optional (zero or more) InputParameters (named InputParameter to avoid confusion with CombinerParameter):

<Policy ...>
   <InputParameter Name="xxx" DataType="...parameter datatype..."><Description>parameter description</Description><AttributeValue DataType="...">some default value</AttributeValue></InputParameter>
   <InputParameter Name="yyy" DataType="...">...</InputParameter>
   ...
</Policy>

The new InputParameter type definition in XACML schema:

<xs:complexType name="InputParameterType">
   <xs:sequence>
          <!-- Parameter description -->
          <xs:element ref="xacml:Description" minOccurs="0"/>
          <!-- Default value / bag of values used if not  -->
          <xs:element ref="xacml:AttributeValue" minOccurs="0"/>
   </xs:sequence>
   <xs:attribute name="ParameterName" type="xs:ID" use="required"/>
   <xs:attribute name="DataType" type="xs:anyURI" use="required"/>
</xs:complexType>

How to use the value of such InputParameter in a Policy/PolicySet? Proposal: add new type of Expression: InputParameterValue

<xs:element name="InputParameterValue" type="xacml:InputParameterValueType" substitutionGroup="xacml:Expression"/>
<xs:complexType name="InputParameterValueType">
   <xs:complexContent>
          <xs:extension base="xacml:ExpressionType">
                <xs:attribute name="ParameterName" type="xs:IDREF"  use="required"/>
          </xs:extension>
   </xs:complexContent>
</xs:complexType>
steven-legg commented 6 months ago

Although this is a way to extend the scope of variable definitions it is more general than that. I would be inclined to rename this as Parameterized Policies and Policy Sets

Ok I changed the issue title.

steven-legg commented 6 months ago

How about Argument instead of InputParameter?

Rather than defining InputParameterValue I suggest replacing ParameterName with:

<xs:attribute name="VariableId" type="xs:string" use="required"/>

and using the existing VariableReference. It's similar to what I did for quantified expressions in the entities profile. There's no need for parameters and variables to be able to use the same names. A name used for a parameter is then not available to use as a name for a variable definition in the same policy.

cdanger commented 6 months ago

How about Argument instead of InputParameter?

I prefer to reserve the term Argument for the actual value passed to a function or - in this case - policy parameter.

Rather than defining InputParameterValue I suggest replacing ParameterName with:

<xs:attribute name="VariableId" type="xs:string" use="required"/>

and using the existing VariableReference. It's similar to what I did for quantified expressions in the entities profile. There's no need for parameters and variables to be able to use the same names. A name used for a parameter is then not available to use as a name for a variable definition in the same policy.

OK here is the updated/simplified proposal then (also renamed InputParameter element to simply Parameter):

New XML type:

<xs:element name ="Parameter" type="ParameterType" />
<xs:complexType name="ParameterType">
   <xs:sequence>
          <xs:element ref="xacml:Description" minOccurs="0"/>
   </xs:sequence>
   <xs:attribute name="ParameterName" type="xs:ID" use="required"/>
   <xs:attribute name="DataType" type="xs:anyURI" use="required"/>
</xs:complexType>

For defining a parameterized policy and passing variables as parameter values (arguments), 2 options:

Option 1 (breaking change but more simple model): Do a breaking change which is adding new element of type Parameter in the Policy type (we also use #11 to merge PolicySet into Policy, and PolicySetIdReference into PolicyIdReference, or use the new type RuleSet):

<xs:complexType name="Policy">
        <xs:sequence>
            ...<!-- new element-->
            <xs:element ref="xacml:Parameter" minOccurs="0" maxOccurs="unbounded"/>
      ...
      </xs:sequence>
...
</xs:complexType>

<!-- New XML type-->
<xs:element name="ParameterizedPolicyReference" type="xacml:ParameterizedPolicyReferenceType"/>
<xs:complexType name="ParameterizedPolicyReferenceType">
        <xs:sequence>
            <xs:element name="PolicyRef" type="xacml:PolicyIdReferenceType" />
           <!-- Pass the parameter values (arguments) -->
            <!--  As an alternative, we could also call it ArgumentExpression-->
            <xs:element name="Apply" minOccurs="0">
                <xs:complexType>
                   <xs:sequence>
                       <xs:element ref="xacml:Expression"  maxOccurs="unbounded"/>
                   </xs:sequence>
              </xs:complexType>
           </xs:element>
      </xs:sequence>
</xs:complexType>

For example:

<Policy PolicyId="A" ...>
  <Parameter Name="xxx" DataType="http://www.w3.org/2001/XMLSchema#string"><Description>parameter description</Description></Parameter>
  <Parameter Name="yyy" DataType="http://www.w3.org/2001/XMLSchema#boolean" />
  ...
</Policy>
<Policy PolicyId="B" ...>
...
    <ParameterizedPolicyReference>
          <PolicyRef>A</PolicyRef>
          <Apply>
               <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">value_of_parameter_xxx</AttributeValue>
               <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#boolean">value_of_parameter_yyy</AttributeValue>
          </Apply>
    </ParameterizedPolicyReference>
</Policy>

Option 2 (backward compatible): New XML types ParameterizedPolicy / ParameterizedPolicySet with the new Parameter elements.

In both options, the parameter is visible as a Variable like any other within the parameterized Policy, so we can simply use a VariableReference to refer to the parameter value. The VariableId must match a Parameter Name.

steven-legg commented 6 months ago

What purpose does the local Apply element serve (apart from causing confusion with the existing Apply element, which will frequently be used to define parameter values)?

    <ParameterizedPolicyReference>
          <PolicyRef>A</PolicyRef>
          <Apply>
               <!-- value_of_parameter_xxx -->
               <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-normalize-space">...</Apply>
               <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#boolean">value_of_parameter_yyy</AttributeValue>
          </Apply>
    </ParameterizedPolicyReference>

Or maybe you meant to call it Arguments?

This would seem to be sufficient (also noting that there isn't a PolicyIdReferenceType) :

<xs:complexType name="ParameterizedPolicyReferenceType">
        <xs:sequence>
            <xs:element ref="xacml:PolicyIdReference"/>
            <xs:element ref="xacml:Expression"  minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
</xs:complexType>

Do you maybe want to associate each parameter value with a parameter name so that the order of the parameter values doesn't matter?

cdanger commented 6 months ago

Yes, you are right, sorry, let's remove the Apply element. At some point, indeed, I wanted to allow passing the arguments in any order, in which case you would have to specify the parameter name, with something like <Apply ParameterName="xxx"> or <ArgumentExpression ParameterName="xxx">the Expression</ArgumentExpression>, then I changed my mind, and I forgot to remove the Apply altogether.

Anyway, as far as I am concerned, the simpler the better, so I agree with your proposal for now:

<xs:complexType name="ParameterizedPolicyReferenceType">
        <xs:sequence>
            <xs:element ref="xacml:PolicyIdReference"/>
            <xs:element ref="xacml:Expression"  minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
</xs:complexType>
cdanger commented 6 months ago

As part of #11, a new Policy type definition is proposed, therefore we should integrate this change (new Parameter element) into the Policy definition proposal of #11.

As for the parameterized policy reference, as part of #16, a new Policy(Id)Reference type definition is proposed, therefore regarding option 1 (breaking change - xacml 4.0), I suggest we take the opportunity to enhance PolicyReferences to support parameterized policies. More info in #16.

cdanger commented 5 months ago

For illustration purposes, here is an example of use case for parameterized policies (maybe not the most trivial one but based on real-life needs, feel free to add others...): in this use case, some main PolicySet root references / calls a parameterized policy Bell-Lapadula from multiple places (<PolicyReference>s) with different arguments (parameter values) in each policy reference. Arguments are expressed as (non-trivial) AttributeSelectors. The Category and/or Path is different in each one.

This also highlights the need to add some indication in Parameter definitions that says whether it is a bag data-type (of integers in this case) or primitive (for PAP's validation purposes in particular). So should we specify in the Parameter definition whether it is a bag or primitive data-type that is expected ? E.g. add an optional boolean attribute like Bag or isBag or MustBeBag which is false - by default - for primitive, and true for bags.

The main policy (some content has been omitted):

<PolicySet xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicySetId="root" Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-unless-permit">
   <PolicySetDefaults>
      <XPathVersion>http://www.w3.org/TR/2007/REC-xpath20-20070123</XPathVersion>
   </PolicySetDefaults>
   <Target/>

   <PolicySet PolicySetId="Permit_iff_all_children_Permit_PS" Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:permit-unless-deny">
      <Target/>
      <Policy PolicyId="Permit_iff_PolicyIdentifiers_match" Version="1.0" RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">
         <Target/>            
         <Rule RuleId="PolicyIdentifiers_match" Effect="Permit">
            <!-- ... (content omitted) ... -->
         </Rule>
      </Policy>
      <PolicySet PolicySetId="Permit_iff_any_organisation-specific_policy_permits" Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:policy-combining-algorithm:deny-unless-permit">
         <Target/>
         <PolicySet PolicySetId="Org_A_MLS_policy"  Version="1.0"   PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">
            <Description>Organisation A's MLS policy</Description>
            <Target>
               <AnyOf>
                  <AllOf>
                     <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Org_A</AttributeValue>
                        <AttributeSelector Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" Path="//*:PolicyIdentifier/text()"  DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
                     </Match>
                  </AllOf>
               </AnyOf>
            </Target>

            <!-- Depending on the issue #16's final decision, the name PolicyReference may be replaced with PolicyIdReference or ParameterizedPolicyReference. -->
            <PolicyReference PolicyId="Bell-Lapadula">
                <!-- The 2 arguments (expressions) passed as the Policy's parameter values. -->
                <AttributeSelector Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#integer"  MustBePresent="true" Path="for $classif_name in //*:Classification[../*:PolicyIdentifier/text() = 'Org_A'] return if ($classif_name = 'TOP SECRET') then 5 else if ($classif_name = 'SECRET') then 4 else if ($classif_name = 'CONFIDENTIAL') then 3 else if ($classif_name = 'RESTRICTED') then 2 else if ($classif_name = 'UNCLASSIFIED') then 1 else 0"/>
                <AttributeSelector Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"  DataType="http://www.w3.org/2001/XMLSchema#integer" MustBePresent="true"  Path="for $classif_name in //*:Classification[../*:PolicyIdentifier/text() = 'Org_A'] return if ($classif_name = 'TOP SECRET') then 5 else if ($classif_name = 'SECRET') then 4 else if ($classif_name = 'CONFIDENTIAL') then 3 else if ($classif_name = 'RESTRICTED') then 2 else if ($classif_name = 'UNCLASSIFIED') then 1 else 0"/>
            </PolicyReference>

         </PolicySet>

         <PolicySet PolicySetId="Org_B_MLS_policy" Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">
            <Description>Organisation B's MLS policy</Description>
            <Target>
               <AnyOf>
                  <AllOf>
                     <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">Org_B</AttributeValue>
                        <AttributeSelector  Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" Path="//*:PolicyIdentifier/text()"  DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
                     </Match>
                  </AllOf>
               </AnyOf>
            </Target>

            <!-- Depending on the issue #16's final decision, the name PolicyReference may be replaced with PolicyIdReference or ParameterizedPolicyReference. -->
            <PolicyReference PolicyId="Bell-Lapadula">
                <!-- The 2 arguments (expressions) passed as the Policy's parameter values. -->
                <AttributeSelector Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#integer" MustBePresent="true"  Path="for $classif_name in //*:Classification[../*:PolicyIdentifier/text() = 'Org_B'] return if  ($classif_name = 'TRES SECRET') then 4 else if ($classif_name = 'SECRET') then 3 else if ($classif_name = 'DIFFUSION RESTREINTE') then 2 else if ($classif_name = 'NON PROTEGE') then 1 else 0"/>
                <AttributeSelector Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#integer" MustBePresent="true" Path="for $classif_name in //*:Classification[../*:PolicyIdentifier/text() = 'Org_B'] return if  ($classif_name = 'TRES SECRET') then 4 else if ($classif_name = 'SECRET') then 3 else if ($classif_name = 'DIFFUSION RESTREINTE') then 2 else if ($classif_name = 'NON PROTEGE') then 1 else 0"/>
            </PolicyReference>

        </PolicySet>

        <PolicySet PolicySetId="Org_C_MLS_policy"  Version="1.0" PolicyCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">
            <Description>Organisation C's MLS policy</Description>
            <!-- ... (content omitted) ... -->
        </PolicySet>

      </PolicySet>
    </PolicySet>
</PolicySet>

The parameterized Policy (used in the previous policy's PolicyReferences):

<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicyId="Bell-Lapadula" Version="1.0" RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit">

    <!-- 
        This Policy takes 2 integer-bag parameters. The attribute 'Bag' specifies whether the parameter value must be a bag or primitive data-type (this attribute name is open for discussion). Each parameter may be used as Variable with the Name as VariableId (like in functions).
    -->
    <Parameter Name="resource_classif_levels" DataType="http://www.w3.org/2001/XMLSchema#integer" Bag="true" />
    <Parameter Name="subject_clearance_levels" DataType="http://www.w3.org/2001/XMLSchema#integer" Bag="true" />

    <Target/>
    <VariableDefinition VariableId="resource_classif_level">
        <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:integer-one-and-only">
            <!-- The Policy parameters can be used as Variables. -->
            <VariableReference VariableId="resource_classif_levels" />
        </Apply>
    </VariableDefinition>
    <VariableDefinition VariableId="action_id">
        <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
           <AttributeDesignator Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
        </Apply>
    </VariableDefinition>

    <Rule RuleId="Bell-Lapadula" Effect="Permit">
        <Condition>
            <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:or">
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                    <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <VariableReference VariableId="action_id"/>
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">READ</AttributeValue>
                    </Apply>
                    <Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:any-of">
             <Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:integer-less-than-or-equal" />
             <VariableReference VariableId="resource_classif_level"/>
             <VariableReference VariableId="subject_clearance_levels"/>
            </Apply>
                </Apply>
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                    <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <VariableReference VariableId="action_id"/>
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">WRITE</AttributeValue>
                    </Apply>
                    <Apply FunctionId="urn:oasis:names:tc:xacml:3.0:function:any-of">
                <Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:integer-greater-than-or-equal" />
            <VariableReference VariableId="resource_classif_level"/>
            <VariableReference VariableId="subject_clearance_levels"/>
            </Apply>
                </Apply>
                </Apply>
            </Apply>
        </Condition>
    </Rule>
</Policy>
steven-legg commented 5 months ago

So should we specify in the Parameter definition whether it is a bag or primitive data-type that is expected ?

Yes we should, for the general case, and to preserve static type checking. I vote for IsBag.

cdanger commented 5 months ago

OK, so the updated schema for Policy Parameters:

<xs:element name ="Parameter" type="ParameterType" />
<xs:complexType name="ParameterType">
  <xs:sequence>
    <!-- Parameter description -->
    <xs:element ref="xacml:Description" minOccurs="0"/>
    <!-- Optional default value / bag of values (set if no argument passed in the policy reference)  -->
    <xs:element ref="xacml:AttributeValue" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="Name" type="xs:ID" use="required"/>
  <xs:attribute name="DataType" type="xs:anyURI" use="required"/>
  <xs:attribute name="IsBag" type="xs:boolean" use="optional" default="false"/>
</xs:complexType>

(New Policy References still in discussion in #16.)

steven-legg commented 5 months ago

What do you think about making AttributeValue an Expression instead? It would allow the default value to be calculated rather than always a literal.

humantypo commented 5 months ago

What about introducing AttributeVar as a way to keep backwards compatibility with AttributeValue and a means by which statics can still be used while allowing expressions…?

steven-legg commented 5 months ago

AttributeValue is in the substitution group for Expression, so the default value can still be an AttributeValue (and will be most of the time).

cdanger commented 5 months ago

What do you think about making AttributeValue an Expression instead? It would allow the default value to be calculated rather than always a literal.

All right then, changing AttributeValue to an Expression. Here is the new Parameter type definition:

<xs:element name ="Parameter" type="ParameterType" />
<xs:complexType name="ParameterType">
  <xs:sequence>
    <!-- Parameter description -->
    <xs:element ref="xacml:Description" minOccurs="0"/>
    <!-- Default value expression (optional), i.e. that is used if no argument passed to this parameter in the policy reference. 
    (See Section 5.25 for the list of elements in the Expression element substitution group.)
    -->
    <xs:element ref="xacml:Expression" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="Name" type="xs:ID" use="required"/>
  <xs:attribute name="DataType" type="xs:anyURI" use="required"/>
  <xs:attribute name="IsBag" type="xs:boolean" use="optional" default="false"/>
</xs:complexType>

AttributeValue is in the substitution group for Expression, so the default value can still be an AttributeValue (and will be most of the time).

Indeed, the list of elements in the Expression subsitution group is listed in section 5.25 of the core spec: <Apply>, <AttributeSelector>, <AttributeValue>, <Function>, <VariableReference> and <AttributeDesignator>.

For the new Policy References supporting parameterized policies, see #16.