random technical thoughts from the Nominet technical team

Verifying ENUM signatures

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5 out of 5)
Loading ... Loading ...
Posted by Anthony on Aug 17th, 2010

When an ENUM user sends us a Create command, we validate the XML against the schemas and that the XML signature chain of trust to our CA is OK. When this doesn’t work, there isn’t much feedback that we can return to the user, and it’s difficult to diagnose what caused the failure.

It’s possible to validate a signature with oXygen but all that says is “Invalid Signature” if there’s an error.

So I’ve put together some Java code which produces a bit more diagnostics; see ValidateEnumCreateJava.zip (or as a .jar file if you don’t have a Java compiler)

Before you start

I recommend doing an XML validity check first: don’t waste time trying to debug XML signature problems if you haven’t.

One way to do this is to use Sun’s Multi Schema Validator - https://msv.dev.java.net/ as suggested in the README in our schema bundles, i.e.
java -jar /path/to/msv.jsr /path/to/nom-enum-root-2.0.xsd your_file.xml

Running the ENUM signature checker

  • compile as:

    javac ValidateEnumCreate.java
  • run as:

    java ValidEnumCreate <yourfile>

or if you don’t have a Java compiler…

  • run from a .jar file:

    java -jar ValidEnumCreate.jar <yourfile>

Results:

Valid Signature

If all is well, the result should be

Signature Validated OK

The response for an invalid signature depends on what was wrong:

Bad DigestValue

If the digest is different but the signature of that digest is correct, the result will be

Signature 0 failed core validation:

Checking that the digest matches the data:
FAIL: DigestValue does not match data
    (Signature 0 ref[’0′] validity status: false)

Checking the signature of the digest:
PASS: SignatureValue verifies DigestValue
    (Signature 0 validation status: true)

This is possibly due to munging of whitespace. The signed XML is fragile and even sensitive to changes in whitespace between tags (I commented on this in an earlier blog article)

Bad Signature or certificate

If the signature is invalid or the wrong certificate is included, the results will be:

Signature 0 failed core validation:

Checking that the digest matches the data:
PASS: DigestValue matches data
    (Signature 0 ref[’0′] validity status: true)

Checking the signature of the digest:
FAIL: SignatureValue does not verify DigestValue
    (Signature 0 validation status: false)
Other errors
  • Failure to parse the XML - error message
  • Failure to decode the Digest/Signature/Certificate - Java exception + stack trace

References

http://jtute.com/java6/0904.html
http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/
http://weblogs.java.net/blog/mullan/archive/2006/01/my_xml_signatur_1.html
http://weblogs.java.net/blog/2007/08/03/even-more-xml-signature-debugging-tips

Signing ENUM XML tokens

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 4 out of 5)
Loading ... Loading ...
Posted by Anthony on Mar 27th, 2009

In order to register an ENUM, a XML token (RFC5105) is signed by a Validation Agency to say that the user has a given phone number & so has the right to a ENUM registration; this token is sent by an ENUM Registrar to Nominet’s EPP server.

This process of signing a token isn’t entirely straightforward:

The simplest way to sign a token is to use the templatesign tool from the Apache XML Security project.

$ templatesign -r testVA.key password -c -x testVA.cert unsigned_token.xml > signed_token.xml

A token signed by templatesign is valid, as can be checked using checksig (from the same toolkit) or Oxygen.  However when this token is included as part of an EPP Create command, the signature becomes invalid.  It seems that templatesign doesn’t do the XML canonicalization quite right.

Aside:
To make an XML signature, a digest is taken of the XML data being signed; the data is “canonicalized” before calculating the digest.  It seems that canonicalisation is more-or-less just a standard way of inserting whitespace.  I’m surprised this is how it works because it’s rather fragile, compared to calculating the digest using the element names, attributes etc & ignoring the formatting.

One workaround for this is to run templatesign on the entire Create.  Although this is satisfactory for testing, it does not work in the situation where a Validation Agency has to send a signed token to a Registrar, who then wraps this up into a Create command & sends that to Nominet’s EPP server.

It is possible to use Oxygen to sign a token (example) and this will do the canonicalization in such a way that when enveloped in a Create command, the signature remains valid.  However, Oxygen is an interactive GUI tool and not suited to making an automated system.

Oxygen says that it uses the Java version of Apache XML Security. Looking at the examples that come with the Apache package (specifically GenEnveloped.java) gives us another option:

We’ve taken this example from the Apache library and made the appropriate changes to sign an ENUM token. I’ve packaged some example code paperclip_24px.png which contains:

  • EnumTokenSigner.java - signs a token in such a way that it will validate even when enveloped in a Create command.  It takes an unsigned token (as a Java String) for input, signs it and returns the signed token
  • EnumTokenCreator.java - is one way to create an unsigned token.  This could be done in any number of ways but using a DOM implementation easily handles absent optional elements.
  • EnumTokenSignExample.java - is a simple test harness - it just loads files & calls the two above utility classes.  You will at least have to edit this to set the path names of the keys & token properties.  It does not (for example) do any of the error-checking that you would expect in production code.
  • a Makefile
Key formats

Java uses a different key format (DER) than OpenSSL (PEM).  It is necessary to convert a key+certificate to this format before using the Java tools.  OpenSSL can perform the conversion:

$ openssl pkcs8 -topk8 -nocrypt -in yourVA_key.key -inform PEM -out yourVA_key.der -outform DER
$ openssl x509 -in yourVA_cert.pem -inform PEM -out yourVA_cert.der -outform DER

RFC 5105 tokens and apache xml security

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5 out of 5)
Loading ... Loading ...
Posted by sion on Sep 26th, 2008

We have been using the apache xml security (java) libraries to produce RFC 5105 type signed XML tokens. There are a number of steps involved which were not obvious (to me) from the documentation or the examples; so here are some of the things that we had to do.

Firstly turn your ascii-armoured private key and certificate into DER format:

openssl x509 -in enum.crt -inform PEM -out enum-cert.der -outform DER
openssl pkcs8 -topk8 -nocrypt -in enum.key -inform PEM -out enum-key.der -outform DER

NB: in this format your private key is unencrypted, if you are more comfortable doing this on the fly then I’m sure that is possible.

In this format you can read the files into your code by doing something like:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new
ByteArrayInputStream(JavaUtils.getBytesFromFile("enum-cert.der")));

and similarly with the private key:

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privKeyBytes);
PrivateKey privKey = (PrivateKey) keyFactory.generatePrivate(privSpec);

where privKeyBytes is a byte array from your enum-key.der.

Now you need a String which contains your unsigned token XML, see rfc5105.

For a starting point, we used the code distributed as GenEnveloped.java in the samples directory. This has a number of issues with it; the KeyInfo is not in X.509 format and the transform lacks the InclusiveNamespaces parameter.

The first issue is dealt with by generating your KeyInfo object like this:

X509Data ki5 = kif.newX509Data(Collections.singletonList(cert));
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(ki5));

Now set up your Reference object like this:

List<Transform> transformList = new ArrayList<Transform>();
transformList.add((fac.newTransform(Transform.ENVELOPED, (C14NMethodParameterSpec) null)));
transformList.add((fac.newTransform
(CanonicalizationMethod.EXCLUSIVE, new ExcC14NParameterSpec(Collections.singletonList("enum-token enum-tokendata")))));

Reference ref = fac.newReference
("#TOKEN", fac.newDigestMethod(DigestMethod.SHA1, null),
transformList, null, null);

The rest of the example code is largely unchanged. This should produce a signed token that you can imbed in other XML documents and still have it validate.

XSLT to map EPP and automaton inputs

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 4 out of 5)
Loading ... Loading ...
Posted by barbara on Dec 20th, 2007

Here at Nominet, we have two interfaces to the registry database:

  • The automaton, an email system which uses PGP signed lists of key-value pairs (KVPs), e.g:

    operation: domain request
    key: auto-example.co.uk
    account-id: 1000001
    dns0: ns0.auto-example.co.uk 96.152.146.245
    dns1: ns1.auto-example.co.uk 96.152.146.246 1234::1234 
  • and EPP, an XML based system. The above request would essentially be written in XML as:
    <epp>
      <create>
         <domain:create>
             <domain:name>auto-example.co.uk</domain:name>
             <domain:account>
                 <domain:account-id>1000001</domain:account-id>
             </domain:account>
            <nameservers>
               <ns:create>
                  <ns:name>ns0.auto-example.co.uk</ns:name>
                  <ns:addr ip="v4">96.152.146.245</ns:addr>
              </ns:create>
              <ns:create>
                  <ns:name>ns0.auto-example.co.uk</ns:name>
                  <ns:addr ip="v4">96.152.146.246</ns:addr>
                  <ns:addr ip="v6">1234::1234</ns:addr>
              </ns:create>
           </nameservers>
        </domain:create>
     </create>
    </epp>

These are two separate processes but basically do the same thing - they write to a database but with a different interface, so we were keen to use as much common code as possible in the system. One way to facilitate this is to use an XSL transformation (using hardware XML acceleration for performance) to convert the XML into KVPs.

Generally, the XSL for this looks like:

<xsl:template match="xml-element-name">key-name:  <xsl:apply-templates/>
	<xsl:text>&#xa;</xsl:text>
</xsl:template>

This tells the XSL processor “whenever you see xml-element-name write out the corresponding key-name and then process the rest of the element”. For leaf elements, processing the rest of the element will get you the XML value added to the output. The &#xa; adds a linebreak.

So, for example:

<xsl:template match="domain:name">key: <xsl:apply-templates/>
    <xsl:text>&#xa;</xsl:text>
</xsl:template>

converts the

<domain:name>auto-example.co.uk</domain:name>

line in the EPP request into a corresponding KVP:

key:auto-example.co.uk

This is fairly straightforward for most fields in the requests. Dealing with nameserver fields is however a little more complicated as the KVPs need to be enumerated and the name and ip addresses need to be included in the same value. To do this, the XSL needs to output the ‘dns#: ‘ part when it encounters an <ns:create> element and then add the name and ip addresses when it encounters the child elements.

To find where the entry sits in the dns enumeration, it is necessary to use XPath within an <xsl:value-of> element to count the position of the <ns:create> amongst its sibling elements:

<xsl:template match="ns:create">dns<xsl:value-of select="position()-1"/>
    <xsl:text>: </xsl:text>
    <xsl:apply-templates/>
 </xsl:template>

This works ok for nameservers until you consider that there is no restriction on the order of the ip addresses in the XML. The order of different elements in EPP requests is specified by the schema, but <ns:addr> elements are distinguished only by their attributes and there are no restrictions on the order. So, it is possible for an ipv6 address to be provided before the ipv4 address and the resultant KVP in the above example:

dns0: ns0.auto-example.co.uk 1234::1234 96.152.146.246

would confuse the KVP processing code. To get around this, we need to tell the XSL processor to go and find the ip addresses in the correct order when it finds the name. Again, we need to use XPath:

<xsl:template match="ns:name">
  <!-- Add the name-->
  <xsl:apply-templates/>

  <!-- Go find the ip addresses-->
  <xsl:if test=
      "count(following-sibling::*[local-name()='addr' and @ip='v4']) > 0">
    <xsl:text> </xsl:text>
    <xsl:value-of select=
                "following-sibling::*[local-name()='addr' and @ip='v4']"/>
  </xsl:if>

  <xsl:if test=
      "count(following-sibling::*[local-name()='addr' and @ip='v6']) > 0">
     <xsl:text> </xsl:text>
     <xsl:value-of select=
                "following-sibling::*[local-name()='addr' and @ip='v6']"/>
   </xsl:if>
   <xsl:text>&#xa;</xsl:text>
</xsl:template>

We don’t need to worry about the key for the KVP here as this has already been done at the <ns:create> element.
So, to find the ipv4 address, we count the number of following siblings with name *:addr and ip attribute of ‘v4‘. If there is one then add it’s value to the KVP.

The Semantic Web: OWL DL datatype issues

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by Al on Jun 6th, 2006

One limitation I found when using Web Ontology Language (OWL) was that it does not support customised datatypes. I realise that the
main purpose of OWL is to recording how data relates to real world objects, but allowing authors to reference XML Schema to define OWL-related
datatypes, while keeping an OWL document in valid DL form would be most useful.

Some work has been undertaken in addressing this (see Working Group Note “XML Schema Datatypes
in RDF and OWL
” - specifically using the id attribute), adding an ID to the XML Schema. then referencing it in the OWL Document:

<owl:DatatypeProperty rdf:ID="domainNameValue">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource=
"http://xml.nominet.org.uk/schema/datatypes#domainName"/>
</owl:DatatypeProperty>

..but using customised datatypes through this method turns documents into OWL Full, which is not ideal. Due to its syntactic freedom,
it is unlikely that future reasoning software will be able to support complete reasoning for OWL Full.

Interestingly enough. further to the W3C reccomendation, a possible extension
OWL-Eu has been suggested to overcome this, but as far as I know this datatype issue hasn’t yet been resolved within the W3C reccomendation,
and until this point reasoning tools will not likely support this.

Real world Semantic Web tools will only support OWL DL due to the limitations/complexities of OWL Lite/Full,
so unless users can create OWL DL documents using customised datatypes, the takeup of OWL will probably be slow.

The Semantic Web: Web Ontology Language (OWL) example

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 4 out of 5)
Loading ... Loading ...
Posted by Al on Jun 6th, 2006

I have recently been exploring the Semantic Web,
looking at a few of the tools,
and also checking out the W3C’s Web Ontology Language (OWL),
by creating a simple example for WHOIS. The W3C also provide a useful overview of the OWL Web ontology language.

Useful tools
Looking at the tools out there, there are several editors/IDEs/validators, both commercial and non-commercial. Of course one can use any
XML editor/notepad to create OWL documents (which are essentially extended RDF), but code completion and validation features are useful.
For a rather exhaustive list, check out the useful if not slightly dated Xml.com
Ontology Tools Survey
(you might need a strong magnifying glass to read the table in it’s PDF form!).

I found the most useful to be SWeDE - an open source Eclipse Plugin.
This includes an OWL editor with helpful features like syntax highlighting, autocompletion, and error-detection.

Validation
A very useful validator I stumbled across was the mindswap OWL Consistency Checker, which allowed
for species validation (Lite, DL or Full) amd checked for ontology consistency.
The ability to validate by URI or pasted in test is handy too. It can also display ontologies in a tree like structure
which could well be useful for more complex ontologies.

Simple OWL example: WHOIS

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rdf:RDF [
<!ENTITY domain  "http://xml.nominet.org.uk/rdf/nom/domain#">
<!ENTITY rdf  "http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#">
<!ENTITY xsd  "http://www.w3.org/2001/XMLSchema#">
<!ENTITY owl  "http://www.w3.org/2002/07/owl#">
]>

<!-- entities are used to make URL modification easy
for the future -->
<rdf:RDF
xmlns="&owl;"
xml:base="&domain;"
xmlns:owl="&owl;"
xmlns:rdfs="&rdfs;"
xmlns:rdf="&rdf;"
xmlns:xsd="&xsd;"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
<!-- ****************** -->
<!-- Ontology metadata -->
<!-- ****************** -->
<owl:Ontology rdf:about="">
<rdfs:comment>This is a simple ontology example to
describe WHOIS</rdfs:comment>
<rdfs:label>Nominet WHOIS Ontology example</rdfs:label>
</owl:Ontology>

<!-- domain name -->
<owl:Class rdf:ID="domainName">
<rdfs:label xml:lang="en">domain name</rdfs:label>
</owl:Class>

<owl:DatatypeProperty rdf:ID="domainNameValue">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<!-- ****************** -->
<!-- registrant -->
<!-- ****************** -->
<owl:Class rdf:ID="registrant">
<rdfs:label>domain name registrant</rdfs:label>
</owl:Class>

<owl:DatatypeProperty rdf:ID="registrantName">
<rdfs:domain rdf:resource="#registrant"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<!-- These are the object properties of a domain name/
registrant -->
<owl:ObjectProperty rdf:ID="hasRegistrant">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="#registrant"/>
<owl:inverseOf rdf:resource="#registrantOf"/>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:ID="registrantOf">
<rdfs:domain rdf:resource="#registrant"/>
<rdfs:range rdf:resource="#domainName"/>
<owl:inverseOf rdf:resource="#hasRegistrant"/>
</owl:ObjectProperty>

<!-- ****************** -->
<!-- trading as -->
<!-- ****************** -->
<owl:Class rdf:ID="tradingAs">
<rdfs:label xml:lang="en">
registrant trading as
</rdfs:label>
</owl:Class>

<owl:ObjectProperty rdf:ID="tradingNameOf">
<rdfs:domain rdf:resource="#registrant"/>
<rdfs:range rdf:resource="#tradingAs"/>
</owl:ObjectProperty>

<!-- ****************** -->
<!-- registration type and it's properties -->
<!-- ****************** -->
<owl:Class rdf:ID="registrantType"/>

<owl:DatatypeProperty rdf:ID="type">
<rdfs:domain rdf:resource="#registrantType"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:ID="numberType">
<rdfs:domain rdf:resource="#registrantType"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:ID="organisationNumber">
<rdfs:domain rdf:resource="#registrantType"/>
<rdfs:range rdf:resource="&xsd;int"/>
</owl:DatatypeProperty>

<!-- registrant type can be either company or charity -->
<owl:Class rdf:ID="companyRegistrant">
<rdfs:subClassOf rdf:resource="#registrantType" />
</owl:Class>

<owl:Class rdf:ID="charityRegistrant">
<rdfs:subClassOf rdf:resource="#registrantType" />
</owl:Class>

<owl:Class rdf:ID="schoolRegistrant">
<rdfs:subClassOf rdf:resource="#registrantType" />
</owl:Class>

<owl:Class rdf:ID="generalRegistrant">
<rdfs:subClassOf rdf:resource="#registrantType" />
</owl:Class>

<!-- define the restrictions that make a registrant type
company or charity -->
<owl:Class rdf:about="#companyRegistrant">
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#type" />
<owl:hasValue rdf:datatype="&xsd;string">UK Limited Company</owl:hasValue>
<owl:minCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:minCardinality>
</owl:Restriction>
</rdfs:subClassOf>

<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#numberType" />
<owl:hasValue rdf:datatype="&xsd;string">Company Number</owl:hasValue>
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>

<owl:Class rdf:about="#charityRegistrant">
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#type" />
<owl:hasValue rdf:datatype="&xsd;string">UK Registered Charity</owl:hasValue>
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#numberType" />
<owl:hasValue rdf:datatype="&xsd;string">Charity Number</owl:hasValue>
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>

<owl:Class rdf:about="#schoolRegistrant">
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#type" />
<owl:hasValue rdf:datatype="&xsd;string">UK Registered School</owl:hasValue>
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#numberType" />
<owl:hasValue rdf:datatype="&xsd;string">Charity Number</owl:hasValue>
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>

<!-- ****************** -->
<!-- Registrant Address -->
<!-- ****************** -->

<owl:Class rdf:ID="registrantAddress">
<rdfs:comment>This is the registrant's recorded address</rdfs:comment>
<rdfs:label>registrant Address</rdfs:label>
</owl:Class>

<owl:DatatypeProperty rdf:ID="addressLine1">
<rdfs:comment>First line of the registrant's address</rdfs:comment>
<rdfs:label>address line 1</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="addressLine2">
<rdfs:comment>Second line of the registrant's address</rdfs:comment>
<rdfs:label>address line 2</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="addressLine3">
<rdfs:comment>Third line of the registrant's address</rdfs:comment>
<rdfs:label>address line 3</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="addressLine4">
<rdfs:comment>Fourth line of the registrant's address</rdfs:comment>
<rdfs:label>address line 4</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="postcode">
<rdfs:comment>Registrant's address postcode</rdfs:comment>
<rdfs:label>registrant postcode</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:ID="country">
<rdfs:comment>Registrant's address country</rdfs:comment>
<rdfs:label>country</rdfs:label>
<rdfs:domain rdf:resource="#registrantAddress"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<!-- ****************** -->
<!-- Registrant's agent -->
<!-- ****************** -->
<!-- The Tag class and its properties -->
<owl:Class rdf:ID="tag" />
<owl:ObjectProperty rdf:ID="hasRegistrationAgent">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="#tag"/>
<owl:inverseOf rdf:resource="#agentFor"/>
</owl:ObjectProperty>

<!-- this next bit could easily be inferred, but is included for completeness -->
<owl:ObjectProperty  rdf:ID="agentFor">
<rdfs:domain rdf:resource="#tag"/>
<rdfs:range rdf:resource="#domainName"/>
<owl:inverseOf rdf:resource="#hasRegistrationAgent"/>
</owl:ObjectProperty>

<owl:DatatypeProperty rdf:ID="publicURL">
<rdfs:comment>tag holder URL</rdfs:comment>
<rdfs:label>publicURL</rdfs:label>
<rdfs:domain rdf:resource="#tag" />
<rdfs:range rdf:resource="&xsd;anyURI"/>
</owl:DatatypeProperty>

<!-- ****************** -->
<!-- relevant dates -->
<!-- ****************** -->
<owl:DatatypeProperty rdf:ID="registeredOn">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;date"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:ID="renewalDue">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;date"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:ID="lastUpdated">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;date"/>
</owl:DatatypeProperty>

<!-- certain dates are mandatory -->
<owl:Class rdf:about="#domainName">
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#registeredOn" />
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="#lastUpdated" />
<owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>

<!-- ****************** -->
<!-- registration status -->
<!-- ****************** -->
<owl:DatatypeProperty rdf:ID="hasStatus">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<!-- ****************** -->
<!-- Name servers -->
<!-- ****************** -->
<!-- add the name server property -->
<owl:DatatypeProperty rdf:ID="nameServer">
<rdfs:domain rdf:resource="#domainName"/>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<!-- specify how many nameservers a domain name can have -->
<owl:Restriction>
<owl:onProperty rdf:resource="#nameServer" />
<owl:minCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:minCardinality>
<owl:maxCardinality rdf:datatype="&xsd;nonNegativeInteger">10</owl:maxCardinality>
</owl:Restriction>

</rdf:RDF>

One bug with SWeDE I found was that it incorrectly flagged references to datatypes: <rdfs:range rdf:resource="&xsd;string"/> as being incorrect when validating.
This seems to have already been logged as a bug, yet to be resolved.
Passing the example through the validator shows it to be correct however.

Why no substitutionGroup for attributes in XML Schema?

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by jay on Mar 9th, 2006

There is a feature of XML Schema called substitutionGroup that is a very neat way to extend schemas. Unfortunately this only applies to elements, not attributes. But before I get to that, here’s a basic explanation of substitutionGroup.

substitutionGroup for elements

In the following example I’ve defined an element <command> based on the type “commandType”. That type in turn is a sequence that so far contains just one element, which is an <abstractCommand>. <abstractCommand> in turn is of the type “abstractExtensionType”, which is defined as an empty type.

<!-- extract from schema1.xsd -->

<element name="command" type="schema1:commandType"/>

<complexType name="commandType">
<sequence>
<element ref="schema1:abstractCommand" />
</sequence>
</complexType>

<element name="abstractCommand"
type="schema1:abstractCommandType" />

<complexType name="abstractCommandType" />

Now none of that is actually usable at the moment because the <abstractCommand> element doesn’t actually contain anything useful. So all we can do is this following pointless code:

<!-- extract from document1.xml -->
<command>
<abstractCommand/>
</command>

However let’s assume this is part of a much larger document, in which case we use this as a place marker. Then we create our second schema, where we provide a concrete command. In this case we define a new element called “create”, which can be used in place of <schema1:abstractCommand> thanks to the substitutionGroup attribute. This is also based on a type that is derived from the type of the element that it is used in place of. In other words <create> is of the type “schema2:extCommandType”, which in turn is an extension of “schema1:abstractCommandType”.

<!-- extract from schema2.xsd -->
<element name="create" substitutionGroup="schema1:abstractCommand"
type="schema2:extCommandType" />

<complexType name="extCommandType">
<complexContent>
<extension base="schema1:abstractCommandType">
<sequence>
<any namespace="##other" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>

So, to finish off the explanation, we can now use all of this in a new document that uses the <schema1:command> element from the first file, but contains the “create” command specified in the second file.

<!-- extract from document2.xml -->
<command>
<create>
<things_to_create/>
</create>
</command>

The final point is that if we had wanted to prevent anyone from create an <abstractCommand> element , just to be tidy, then we could have defined it using the “abstract” attribute:

<complexType name="abstractCommandType" abstract="true" />

substitutionGroup for attributes

XML Schema does not actually support substitutionGroup for attributes, but if it did then what would it look like? Well a bit like this:

<!-- extract from schema1.xsd -->

<complexType name="operation">
<sequence>
<any namespace="##other" />
</sequence>
<attribute ref="schema1:opcode" use="required" />
</complexType>

<attribute name="opcode" type="schema1:opcodeType" />

<simpleType name="opcodeType">
<restriction base="token">
<enumeration value="request">
<enumeration value="approve">
<enumeration value="reject">
<enumeration value="withdraw">
</restriction>
</simpleType>

<!-- extract from schema2.xsd -->

<simpleType name="extendedOpcodeType">
<union memberTypes="schema1:opcodeType">
<simpleType>
<restriction base="token">
<enumeration value="dispute">
</restriction>
</simpleType>
</union>
</simpleType>

<attribute name="extendedOpcode" type="schema2:extendedOpcodeType"
substitutionGroup="schema1:opcode" />

<!-- extract from document1.xml -->

<operation extendedOpcode="dispute">
<blah />
</operation>

I’ve proposed this to the XML Schema working group to see if they might consider it for v1.1

Further reading

If you are interested in reading more about XML Schema or substitutionGroup then don’t bother looking these up in the XML Nutshell book as these only get a cursory section. Use a specialist XML Schema book instead.

Recent Posts

Highest Rated

Categories

Archives

Meta: