Skip to content

Proposal for UML‐based agnostic Core model and XML JSON schema mapping rules

Cyril Dangerville edited this page Mar 22, 2025 · 11 revisions

To support the discussion #38, here is a UML class diagram proposed as agnostic model for the Policy syntax, based on current XACML 4.0 draft schema and latest issues (Request/Response data models still need to be added to get a complete XACML model):

UML class diagram

This class diagram relies on the new UML profile below for the definitions of the custom stereotypes (RestrictedString, Meta, etc.):

UML profile

The choice of UML is mainly to have a graphical notation and a corresponding machine-readable data model that can be translated to XML schema and JSON schema as priority targets, but also schemas/grammars for other formats in the future as the need arises (YAML, CBOR...). Note that the JSON schema may be used for YAML as well to some extent.

To explore the UML diagrams in more details, you may import the projects in the ZIP file into Eclipse Papyrus (tested with v6.7.0 - 2024-06).

The next sections detail the mapping rules for mapping this UML model to the XML/JSON schemas (version Draft 2020-12 of JSON schema is used as reference):

UML Primitive Types

UML standard types

Mapping rules for standard UML primitive types:

UML XML schema JSON schema
String xs:string string
Boolean xs:boolean boolean
Integer xs:integer integer

UML RestrictedString

A UML class Foo stereotyped as <<RestrictedString>> represents a subtype of String restricted by a regular expression R, and this regex is specified as the pattern property value. The regex delimiters ^$ are implicit in this case, i.e. this pattern is matched against the whole string. (like in XSD simpleType restriction's pattern element).

XML schema mapping

If the class name is URI (resp. NCName), it is mapped to the standard xs:anyURI (resp. xs:NCName) type.

Else it is mapped to:

     <xs:simpleType name="FooType">
		<xs:restriction base="xs:string">
			<xs:pattern value="R"/>
		</xs:restriction>
	 </xs:simpleType>

This covers the following XML types used in XACML schema:

  • xs:anyURI,
  • xs:NCName,
  • xacml:Version,
  • xacml:VersionMatch.

JSON schema mapping

If the class name is URI, it is mapped to:

{ "type": "string", "format": "uri" }

Else it is mapped to a new subschema (regex delimiters ^$ must be explicit in the pattern expression):

"$defs": {
    ...
    "Foo": { "type": "string", "pattern": "^R$" }
    ...
}

UML Enumeration

A UML class Foo stereotyped as <<Enumeration>> represents a subtype of String that is an enumeration of string literals X, Y, etc.

XML schema mapping

<xs:simpleType name="FooType">
    <xs:restriction base="xs:string">
        <xs:enumeration value="X"/>
        <xs:enumeration value="Y"/>
        ...
    </xs:restriction>
</xs:simpleType>

This covers the following XML type(s) used in XACML schema:

  • xacml:Effect.

If there is at least one Datatype with a non-meta property (i.e. property not stereotyped Meta) of type Foo, then the definition above must be preceded by a definition of an element Foo with type FooType (non-meta properties will be references to this element) as follows:

<xs:element name="Foo" type="xacml:FooType"/>
<xs:simpleType name="FooType">
    <xs:restriction base="xs:string">
        <xs:enumeration value="X"/>
        <xs:enumeration value="Y"/>
        ...
    </xs:restriction>
</xs:simpleType>

This covers the following XML types / elements used in XACML schema:

  • xacml:DecisionType / xacml:Decision.

JSON schema mapping

"$defs": {
    ...
    "Foo": { "enum": ["X", "Y", ...] }
    ...
}

UML Datatypes

A UML Datatype here is a class stereotyped as <<Datatype>>, which represents a complex datatype as opposed to Primitive types described in the previous section, i.e. datatypes that can have properties.

Terms:

  • Abstract Datatype: Datatype whose abstract modifier is set to true and the title is italicized on the UML diagram.
  • Empty Datatype: a datatype is said empty if it has no property in its definition, regardless of subtypes. E.g. PolicyCombinedItem, Expression.
  • Meta-property / non-meta property: a meta-property is a UML Datatype property stereotyped as Meta (marked <<Meta>> in the UML diagram). Other properties are referred to as non-meta properties.

XML schema mapping

Anonymized Datatype (ignored)

If an UML Datatype Foo is an empty abstract Datatype used no more than once as property type in the whole data model (such as PolicyCombinedItem), it is anonymized, i.e. not mapped to any corresponding XML type or element. It is only the (single) property of type Foo of some other Datatype, if there is any, that is mapped to a xs:choice of Foo's subtypes, as explained in the Property mapping rules below. So Foo is completely absent from the resulting XML schema.

Non-anonymized Datatype mapping

Else (Foo is not anonymized):

  1. Foo is mapped to a globally defined xs:complexType named FooType:

    <xs:complexType name="FooType">...</xs:complexType>
  2. If Foo is stereotyped <<MixedContent>>, this refers to XML Mixed Content (character data interspersed with child elements), i.e. mixed="true" must be specified on the xs:complexType:

    <xs:complexType name="FooType" mixed="true">
        ...
    </xs:complexType>

    This covers the following XML type(s) used in XACML schema:

    • xacml:ContentType,
    • xacml:AttributeValueType,
    • xacml:AttributeAssignmentType.

    N.B.: according to the table Complex Type Definition with complex content Schema Component of section 3.4.2 XML Representation of Complex Type Definitions of W3C XML schema specification, the mixed attribute on complexContent is redudant if already defined on parent complexType.

  3. If Foo is abstract, then the attribute abstract="true" must be specified on the complexType:

    <xs:complexType name="FooType" abstract="true">...</xs:complexType>

    This covers the following XML type(s) used in XACML schema:

    • xacml:IdReferenceType,
    • xacml:ExpressionType.
  4. If Foo inherits from another Datatype - the supertype - Bar (Generalization relationship) that is not anonymized either, then add the corresponding xs:complexContent/xs:extension to the FooType complexType definition:

    <xs:complexType name="FooType">
    	<xs:complexContent>
    		<xs:extension base="xacml:BarType">
    			...
    		</xs:extension>
    	</xs:complexContent>
    </xs:complexType>
  5. If there is at least one non-meta property of type Foo (and again, Foo is not an anonymized Datatype as defined earlier), then the xs:complexType definition must be preceded by a globally defined xs:element named Foo with type FooType in XACML namespace (so that the other Datatype(s)' non-meta properties can make references to it as explained later in the Property mapping rules) as follows:

     <xs:element name="Foo" type="xacml:FooType"/>
     <xs:complexType name="FooType">...</xs:complexType>
  6. Plus, if the UML Datatype is abstract (as defined earlier), this xs:element must also have the attribute abstract="true" specified:

    <xs:element name="Foo" type="xacml:FooType" abstract="true"/>
    <xs:complexType name="FooType" abstract="true">...</xs:complexType>

    This covers the following XML type(s) / element(s) used in XACML schema:

    • xacml:ExpressionType / xacml:Expression.
  7. Plus, if Foo inherits from a Datatype Bar and a global element named Bar is defined according to the mapping rule #5 above, then add the substitutionGroup="xacml:Bar" attribute:

    <xs:element name="Foo" type="xacml:FooType" substitutionGroup="xacml:Bar"/>
    <xs:complexType name="FooType">
    	<xs:complexContent>
    		<xs:extension base="xacml:BarType">
    			...
    		</xs:extension>
    	</xs:complexContent>
    </xs:complexType>

    N.B.: the substitutionGroup attribute can be multi-valued (so multi-inheritance is possible).

  8. Property mapping rules: for each property in the UML Datatype,

    • 8.1. If the property is stereotyped as <<Meta>> (meta-property), convert to an XML attribute as follows:

      • The attribute name is the UML property name converted to Pascal case;
      • The attribute type is the corresponding XML type according to the mapping rules defined in the previous UML Primitive Types section;
      • If the property's multiplicity is 0..1, specify use="optional", else use="required";
      • If the property has a default value, add the same default value to the attribute.

      For example, a meta-property myProperty of type Boolean, multiplicity 0..1, default value false would translate to:

      <xs:attribute name="MyProperty" type="xs:boolean" use="optional" default="false"/>

      A meta-property myProperty of type Effect, multiplicity 1 (no default value) would translate to:

      <xs:attribute name="MyProperty" type="xacml:EffectType" use="required"/>
    • 8.2. Else (the property is not stereotyped as <<Meta>>):

      • 8.2.1. If the property type - say PropType - is anonymized (cf. previous section), then map to an XML choice between the XML elements corresponding to all PropType's subtypes. The property's multiplicity is mapped to the xs:choice's minOccurs/maxOccurs similarly to the previous rules for meta-properties:

        • If the property's multiplicity is 0..1, map to minOccurs="0" (maxOccurs is 1 by default);
        • If the multiplicity is *, map to minOccurs="0" maxOccurs="unbounded";
        • If the multiplicity is 1, do not specify minOccurs/maxOccurs as 1 is the default value for both;
        • If the multiplicity is 1..*, map to minOccurs="1" maxOccurs="unbounded".

        For example, considering that the subtypes of the Datatype PolicyCombinedItem are Policy, PolicyReference, VariableDefinition and Rule, the Policy Datatype's combinedItems (non-meta) property with type PolicyCombinedItem (abstract) and multiplicity * will be mapped to:

        <xs:choice minOccurs="0" maxOccurs="unbounded">
        	<xs:element ref="xacml:Policy"/>
        	<xs:element ref="xacml:PolicyReference"/>
        	<xs:element ref="xacml:VariableDefinition"/>
        	<xs:element ref="xacml:Rule"/>
        </xs:choice>
      • 8.2.2. Else (the property type is not abstract or at least one other property in the data model have the same type) convert to an XML element refering to the element that has the same name as the property type, in the XACML namespace, and map the property's multiplicity to minOccurs/maxOccurs values as follows (same as above):

        • If the property's multiplicity is 0..1, map to minOccurs="0" (maxOccurs is 1 by default);
        • If the multiplicity is *, map to minOccurs="0" maxOccurs="unbounded";
        • If the multiplicity is 1, do not specify minOccurs/maxOccurs as 1 is the default value for both;
        • If the multiplicity is 1..*, map to minOccurs="1" maxOccurs="unbounded".

        For example, a non-meta property myProperty of type Foo, multiplicity * would translate to:

        <xs:element ref="xacml:Foo" minOccurs="0" maxOccurs="unbounded"/>
  9. If Foo is stereotyped with <<AnyMetaProperty>>, it allows to have any meta-property besides the meta properties explicitly defined in the class if there is any, which means any XML attribute may be added. This translates to a xs:anyAttribute element at the end of the list of XML attributes:

        <xs:complexType name="FooType" ...>
        ...
            <!-- Other attributes mapped from meta-properties of the UML class, if any. -->
    		<xs:anyAttribute namespace="##any" processContents="lax"/>
            ...
        </xs:complexType>

    This covers the following XML type(s) used in XACML schema:

    • xacml:AttributeValueType.
  10. If Foo is stereotyped with <<AnyProperty>>, it allows to have any non-meta property besides the non-meta properties explicitly defined in the class if there is any, which means any child XML element may be added to the element sequence. This translates to a xs:any element at the end of the XML sequence; and its multiplicity (number of occurrences) depends on the value specified for the AnyProperty stereotype's oneAndOnly property:

    • 10.1. If oneAndOnly="true" (default value), this is equivalent to minOccurs = maxOccurs = 1 (one and only one) which may be omitted as these are the default values, so this translates to:

      <xs:complexType name="FooType" ...>
      ...
          <xs:sequence>
              <!-- Other elements mapped from non-meta properties of the UML class, if any. -->
              <xs:any namespace="##any" processContents="lax"/>
          </xs:sequence>
          ...
      </xs:complexType>

      This covers the following XML type(s) used in XACML schema:

      • xacml:ContentType.
    • 10.2. If oneAndOnly="false" (zero or more), this is equivalent to minOccurs=0 and maxOccurs=unbounded:

      <xs:complexType name="FooType" ...>
      ...
          <xs:sequence>
              <!-- Other elements based on explicit properties of the UML class, if any. -->
              <xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
          </xs:sequence>
      ...
      </xs:complexType>

      This covers the following XML type(s) used in XACML schema:

      • xacml:AttributeValueType,
      • xacml:StatusDetailType.

JSON schema mapping

  1. Apply mapping rules based on the JSON Profile: the JSON schema of certain UML Datatypes is defined by the JSON Profile of XACML 3.0 (the JSON Profile will need some update to align with new XACML 4.0 model):

    • Content: defined as JSON string according to the JSON profile, section 4.2.2, 4.2.3.

      {"type": "string"}
    • AttributeValue (4.2.4):

      "AttributeValue":
      {
      	"description": "Security warning: this definition allows any JSON object as value. TODO: find a way to validate it somehow. Possible solutions: 1) Modify this schema in production to restrict possible values as much as possible. 2) Any equivalent of XML processContents='strict'. 3) Any JSON processor that enforces a max text length, max number of keys, max object depth.",
      	"anyOf": 
      	[
      		{
      			"type": "array",
      			"items": 
      			{
      				"type": "boolean"
      			},
      			"minItems": 0
      		},
      		{
      			"type": "array",
      			"items": 
      			{
      				"type": ["number"]
      			},
      			"minItems": 0
      		},
                              {
      			"type": "array",
      			"items": 
      			{
      				"type": ["string"]
      			},
      			"minItems": 0
      		},
      		{
      			"type": "array",
      			"items": 
      			{
      				"type": "object"
      			},
      			"minItems": 0
      		}
      	]
      }
    • Attribute (4.2.4):

      "Attribute":
      {
      	"type": "object",
      	"properties": 
      	{
      		"AttributeId": 
      		{
      			"type": "string",
      			"format": "uri-reference"
      		},
      		"Issuer": 
      		{
      			"type": "string"
      		},
      
      		"IncludeInResult": 
      		{
      			"type": "boolean"
      		},
      
      		"DataType": 
      		{
      			"type": "string",
      			"format": "uri-reference",
                                      "default": "http://www.w3.org/2001/XMLSchema#string"
      		},
      
      		"Value": 
      		{
      			"$ref": "#/defs/AttributeValue"
      		}
      	},
      
      	"required": 
      	[
      		"AttributeId",
      		"Value"
      	],
      
      	"additionalProperties": false
      }

      N.B.: according to the resolution of issue #18, the Attribute with IncludeInResult is renamed to RequestAttribute, whereas the Attribute in Result and PolicyIssuer does not have IncludeInResult anymore.

    • To be defined: Request, Response, Result, Status, MissingAttributeDetail, StatusCode, AttributeAssignment.

    Note: all MixedContent Datatypes (Content, AttributeValue, AttributeAssignment ) are already addressed in Special cases above.

  2. Else (the UML Datatype is not in the previous list):

    • 1.1. If the UML Datatype is stereotyped <<MixedContent>>, raise an error Unsupported as no other MixedContent datatype is allowed besides the Content, AttributeValue and AttributeAssignment in the previous list.

    • 1.2. Else (not MixedContent), skip to the next rule.

  3. If the UML Datatype Foo is abstract,

    • 3.1. If Foo is empty (no property) (e.g. Expression, PolicyCombinedItem), map to a oneOf composition of the schema references to all of Foo's subtypes' subschemas, including indirect subtypes', which implies that each subtype of Foo must have one:

          "$defs": {
              ...
              "Foo": { 
                  "oneOf": [    
                      { "$ref": "#/$defs/Subtype1" },
                      { "$ref": "#/$defs/Subtype2" },
                      ...  
                  ] 
              }
              ...
          }

      For example, the Expression Datatype is mapped to:

      "$defs": {
           ...
           "Expression": { 
               "oneOf": [    
                   { "$ref": "#/$defs/AttributeValue" },
                   { "$ref": "#/$defs/AttributeDesignator" },
                   { "$ref": "#/$defs/Function" },
                   { "$ref": "#/$defs/Apply" }
                   ...  
               ] 
           }
           ...
      }
    • 3.2. Else (Foo is not empty, e.g. IdReference Datatype), Foo is mapped to the following (the subtypes will use FooBase type as actual subtype in the JSON schema as shown in the next mapping rules):

      "$defs": {
          ...
          "FooBase": { 
              "type": "object",
              "properties": {
                  ...the properties...
              },
              "required": [...properties with multiplicity 1 or more...],
              "additionalProperties": false
          },
      
          "Foo": { 
              "oneOf": [    
                  { "$ref": "#/$defs/Subtype1" },
                  { "$ref": "#/$defs/Subtype2" },
                  ...  
              ] 
          }
          ...
      }
  4. Else (Foo is not abstract):

    • 4.1. If it does not inherit from another Datatype or inherits from an empty (abstract or not) Datatype, then map to a JSON object type as follows (the empty supertype is ignored, and "additionalProperties": false by default):
      "$defs": {
          ...
          "Foo": { 
              "type": "object",
              "properties": {
                  ...the properties...
              },
              "required": [...properties with multiplicity 1 or more...]
              "additionalProperties": false
          }
          ...
      }
    • 4.2. Else (Foo inherits a non-empty Datatype Bar):
      • 4.2.1. If the (non-empty) supertype Bar is abstract, considering a new BarBase type has been defined according to previous rule 3.2, then map Foo to the following schema:

        "$defs": {
                ...
                    
            "Foo": { 
                "allOf": [    
                    { "$ref": "#/$defs/BarBase" },
                    { 
                        "type": "object",
                        "properties": {
                            ...the properties...
                        }
                        "required": [...]
                        "additionalProperties": false
                    } 
                ] 
            }
            ...
        }
      • 4.2.2. Else (Bar supertype is not abstract), then use Bar (instead of BarBase) as base type:

        "$defs": {
                ...
                    
            "Foo": { 
                "allOf": [    
                    { "$ref": "#/$defs/Bar" },
                    { 
                        "type": "object",
                        "properties": {
                            ...the properties...
                        }
                        "required": [...]
                        "additionalProperties": false
                    } 
                ] 
            }
            ...
        }
  5. Property mapping rules: for each property in the UML Datatype,

    • 5.1. The Property is mapped to a JSON property in the JSON object type definition as follows:
      • The JSON property name is the same as the UML property name.

        [!NOTE]
        Should we use Pascal case like the XACML/XML schema instead? Isn't this awkward for JSON?

        [!WARNING]
        This is not strictly equivalent to the UML model since the Meta stereotype is ignored (no difference between meta and non-meta properties in JSON).

      • The UML property type Foo is mapped to a JSON subschema or a reference to one, as follows:

        • If Foo is one of the UML Primitive Types that can be mapped to a JSON standard type/format (see UML Primitive Types section), e.g. string/uri for UML URI type, then ("format" is optional) use a subschema:
           { "type": "matching_standard_json_datatype", "format": "matching_standard_format_if_necessary" }

        Else there must be a corresponding subschema Foo as defined by previous rules, in which case you use a subschema reference instead:

           { "$ref": "#/$defs/Foo" }
    • 5.2. The property's multiplicity is mapped as follows:
      • 0..1: do not include the property name in the required array of the subschema definition;
            "properties": {
                ...
                "myProperty": { ...Foo's subschema or subschema ref (see previous rule)... },
                ...
            }
        e.g. for two UML properties, one of which does not have a corresponding standard type/format:
            "properties": {
                "myProperty1": { "type": "string", "format": "uri" },
                "myProperty2": { "$ref": "#/$defs/Foo" }
            }
      • 1: same as above, but in this case, the property name must be included in the required array.
            "properties": {
                ...
                "myProperty": { ...Foo's subschema or subschema ref (see first property mapping rule)... },
                ...
            },
            "required": ["myProperty", ...]
      • *: the JSON property type is an array of the Foo's JSON schema type definition but the property name is not included in the required array of the subschema:
             "properties": {
                 ...
                 "myProperty": { 
                     "type": "array",
                     "items": {...Foo's subschema or subschema ref (see first property mapping rule)...},
                     "unevaluatedItems": false
                 },
                 ...
             }
        e.g. for two multi-valued UML properties, one of which does not have a corresponding standard type/format:
            "properties": {
                "myProperty1": { "type": "array", "items": {"type": "string", "format": "uri"}, "unevaluatedItems": false },
                "myProperty2": { "type": "array", "items": {"$ref": "#/$defs/Foo"}, "unevaluatedItems": false }
            }
      • 1..*: same as above, but the array type definition has "minItems": 1 and the property name must be included in the required array of the subschema:
             "properties": {
                 ...
                 "myProperty": { 
                     "type": "array",
                     "minItems": 1,
                     "items": {...Foo's subschema or subschema ref (see previous rule)...},
                     "unevaluatedItems": false
                 },
                 ...
             },
             "required": ["myProperty", ...]
    • 5.3. If the UML property has a default value, use the default keyword in JSON schema to specify the default value as well.
  6. If the UML Datatype Foo is stereotyped as <<AnyMetaProperty>> (allows any meta-property besides defined ones if there is any, i.e. any JSON property may be added), then set "additionalProperties"=true in Foo's subschema.

    • If Foo is also stereotyped as AnyProperty and AnyProperty stereotype's property oneAndOnly is true (one and only one other property is allowed), then return an error Unsupported as this case is not supposed to exist in the data model.

    Else (Foo has no AnyMetaProperty stereotype):

    • 6.1. If Foo is stereotyped as <<AnyProperty>> (allows any non-meta property besides defined ones), then:
      • 6.1.1. If AnyProperty stereotype's oneAndOnly="false" (i.e. it allows any property besides the Datatype's defined properties if there is any, which means any JSON property may be added in the schema), then set "additionalProperties"=true in Foo's schema.

      • 6.1.2. Else (oneAndOnly=true, i.e. only one other property): set "additionalProperties"=false, and:

        • 6.1.2.1. If Foo has optional properties: return the error Unsupported (not allowed in the data model) (Content Datatype is the only AnyProperty with oneAndOnly=true in the whole data model and indeed has no optional property)
        • 6.1.2.2. Else (Foo has no optional property, only required properties) set "minProperties" = "maxProperties" = number_of_defined_properties + 1 in the Foo's schema.