fix speaks for auth
[sfa.git] / sfa / trust / auth.py
index 18c3d61..c5f22f4 100644 (file)
-#
-# SfaAPI authentication 
-#
-import sys
-
-from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \
-    BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied, CredentialNotVerifiable, Forbidden, \
-    BadArgs
-from sfa.util.sfalogging import logger
-from sfa.util.config import Config
-from sfa.util.xrn import Xrn, get_authority
-
-from sfa.trust.gid import GID
-from sfa.trust.rights import Rights
-from sfa.trust.certificate import Keypair, Certificate
-from sfa.trust.credential import Credential
-from sfa.trust.trustedroots import TrustedRoots
-from sfa.trust.hierarchy import Hierarchy
-from sfa.trust.sfaticket import SfaTicket
-
-
-class Auth:
-    """
-    Credential based authentication
-    """
-
-    def __init__(self, peer_cert = None, config = None ):
-        self.peer_cert = peer_cert
-        self.hierarchy = Hierarchy()
-        if not config:
-            self.config = Config()
-        self.load_trusted_certs()
-
-    def load_trusted_certs(self):
-        self.trusted_cert_list = TrustedRoots(self.config.get_trustedroots_dir()).get_list()
-        self.trusted_cert_file_list = TrustedRoots(self.config.get_trustedroots_dir()).get_file_list()
-
-    def checkCredentials(self, creds, operation, xrns=[], check_sliver_callback=None, speaking_for_hrn=None):
-
-        def log_invalid_cred(cred):
-            cred_obj=Credential(string=cred)
-            logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
-            error = sys.exc_info()[:2]
-            return error
-
-        # if xrns are specified they cannot be None or empty string
-        if xrns:
-            for xrn in xrns:
-                if not xrn:
-                    raise BadArgs("Invalid urn or hrn")
-
-        
-        if not isinstance(xrns, list):
-            xrns = [xrns]
-
-        slice_xrns = Xrn.filter_type(xrns, 'slice')
-        sliver_xrns = Xrn.filter_type(xrns, 'sliver')
-
-        # we are not able to validate slivers in the traditional way so 
-        # we make sure not to include sliver urns/hrns in the core validation loop
-        hrns = [Xrn(xrn).hrn for xrn in xrns if xrn not in sliver_xrns] 
-        valid = []
-        speaks_for_cred = None
-        if not isinstance(creds, list):
-            creds = [creds]
-        logger.debug("Auth.checkCredentials with %d creds on hrns=%s"%(len(creds),hrns))
-        # won't work if either creds or hrns is empty - let's make it more explicit
-        if not creds: raise Forbidden("no credential provided")
-        if not hrns: hrns = [None]
-        error=[None,None]
-        for cred in creds:
-            for hrn in hrns:
-                try:
-                    self.check(cred, operation, hrn)
-                    valid.append(cred)
-                except:
-                    if speaking_for_hrn:
-                       try:
-                          self.check(cred, operation, speaking_for_hrn)
-                          speaks_for_cred = cred
-                          valid.append(cred)
-                       except:
-                          error = log_invalid_cred(cred)
-                    else:
-                       error = log_invalid_cred(cred)
-                    continue
-        
-        # make sure all sliver xrns are validated against the valid credentials
-        if sliver_xrns:
-            if not check_sliver_callback:
-                msg = "sliver verification callback method not found." 
-                msg += " Unable to validate sliver xrns: %s" % sliver_xrns
-                raise Forbidden(msg)
-            check_sliver_callback(valid, sliver_xrns)
-                
-        if not len(valid):
-            raise Forbidden("Invalid credential %s -- %s"%(error[0],error[1]))
-        
-        if speaking_for_hrn and not speaks_for_cred:
-            raise InsufficientRights('Access denied: "geni_speaking_for" option specified but no valid speaks for credential found: %s -- %s' % (error[0],error[1]))
-        
-        return valid
-        
-        
-    def check(self, credential, operation, hrn = None):
-        """
-        Check the credential against the peer cert (callerGID included 
-        in the credential matches the caller that is connected to the 
-        HTTPS connection, check if the credential was signed by a 
-        trusted cert and check if the credential is allowed to perform 
-        the specified operation.    
-        """
-        cred = Credential(cred=credential)    
-        self.client_cred = cred
-        logger.debug("Auth.check: handling hrn=%s and credential=%s"%\
-                         (hrn,cred.get_summary_tostring()))
-
-        if cred.type not in ['geni_sfa']:
-            raise CredentialNotVerifiable(cred.type, "%s not supported" % cred.type)
-        self.client_gid = self.client_cred.get_gid_caller()
-        self.object_gid = self.client_cred.get_gid_object()
-        
-        # make sure the client_gid is not blank
-        if not self.client_gid:
-            raise MissingCallerGID(self.client_cred.get_subject())
-       
-        # validate the client cert if it exists
-        if self.peer_cert:
-            self.verifyPeerCert(self.peer_cert, self.client_gid)                   
-
-        # make sure the client is allowed to perform the operation
-        if operation:
-            if not self.client_cred.can_perform(operation):
-                raise InsufficientRights(operation)
-
-        if self.trusted_cert_list:
-            self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
-        else:
-           raise MissingTrustedRoots(self.config.get_trustedroots_dir())
-       
-        # Make sure the credential's target matches the specified hrn. 
-        # This check does not apply to trusted peers 
-        trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
-        if hrn and self.client_gid.get_hrn() not in trusted_peers:
-            target_hrn = self.object_gid.get_hrn()
-            if not hrn == target_hrn:
-                raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
-                                       (target_hrn, hrn) )       
-        return True
-
-    def check_ticket(self, ticket):
-        """
-        Check if the tickt was signed by a trusted cert
-        """
-        if self.trusted_cert_list:
-            client_ticket = SfaTicket(string=ticket)
-            client_ticket.verify_chain(self.trusted_cert_list)
-        else:
-           raise MissingTrustedRoots(self.config.get_trustedroots_dir())
-
-        return True 
-
-    def verifyPeerCert(self, cert, gid):
-        # make sure the client_gid matches client's certificate
-        if not cert.is_pubkey(gid.get_pubkey()):
-            raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())            
-
-    def verifyGidRequestHash(self, gid, hash, arglist):
-        key = gid.get_pubkey()
-        if not key.verify_string(str(arglist), hash):
-            raise BadRequestHash(hash)
-
-    def verifyCredRequestHash(self, cred, hash, arglist):
-        gid = cred.get_gid_caller()
-        self.verifyGidRequestHash(gid, hash, arglist)
-
-    def validateGid(self, gid):
-        if self.trusted_cert_list:
-            gid.verify_chain(self.trusted_cert_list)
-
-    def validateCred(self, cred):
-        if self.trusted_cert_list:
-            cred.verify(self.trusted_cert_file_list)
-
-    def authenticateGid(self, gidStr, argList, requestHash=None):
-        gid = GID(string = gidStr)
-        self.validateGid(gid)
-        # request_hash is optional
-        if requestHash:
-            self.verifyGidRequestHash(gid, requestHash, argList)
-        return gid
-
-    def authenticateCred(self, credStr, argList, requestHash=None):
-        cred = Credential(string = credStr)
-        self.validateCred(cred)
-        # request hash is optional
-        if requestHash:
-            self.verifyCredRequestHash(cred, requestHash, argList)
-        return cred
-
-    def authenticateCert(self, certStr, requestHash):
-        cert = Certificate(string=certStr)
-        # xxx should be validateCred ??
-        self.validateCred(cert)   
-
-    def gidNoop(self, gidStr, value, requestHash):
-        self.authenticateGid(gidStr, [gidStr, value], requestHash)
-        return value
-
-    def credNoop(self, credStr, value, requestHash):
-        self.authenticateCred(credStr, [credStr, value], requestHash)
-        return value
-
-    def verify_cred_is_me(self, credential):
-        is_me = False 
-        cred = Credential(string=credential)
-        caller_gid = cred.get_gid_caller()
-        caller_hrn = caller_gid.get_hrn()
-        if caller_hrn != self.config.SFA_INTERFACE_HRN:
-            raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
-
-        return   
-        
-    def get_auth_info(self, auth_hrn):
-        """
-        Given an authority name, return the information for that authority.
-        This is basically a stub that calls the hierarchy module.
-        
-        @param auth_hrn human readable name of authority  
-        """
-
-        return self.hierarchy.get_auth_info(auth_hrn)
-
-
-    def veriry_auth_belongs_to_me(self, name):
-        """
-        Verify that an authority belongs to our hierarchy. 
-        This is basically left up to the implementation of the hierarchy
-        module. If the specified name does not belong, ane exception is 
-        thrown indicating the caller should contact someone else.
-
-        @param auth_name human readable name of authority
-        """
-
-        # get auth info will throw an exception if the authority doesnt exist
-        self.get_auth_info(name)
-
-
-    def verify_object_belongs_to_me(self, name):
-        """
-        Verify that an object belongs to our hierarchy. By extension,
-        this implies that the authority that owns the object belongs
-        to our hierarchy. If it does not an exception is thrown.
-    
-        @param name human readable name of object        
-        """
-        auth_name = self.get_authority(name)
-        if not auth_name:
-            auth_name = name 
-        if name == self.config.SFA_INTERFACE_HRN:
-            return
-        self.verify_auth_belongs_to_me(auth_name) 
-             
-    def verify_auth_belongs_to_me(self, name):
-        # get auth info will throw an exception if the authority doesnt exist
-        self.get_auth_info(name) 
-
-
-    def verify_object_permission(self, name):
-        """
-        Verify that the object gid that was specified in the credential
-        allows permission to the object 'name'. This is done by a simple
-        prefix test. For example, an object_gid for plc.arizona would 
-        match the objects plc.arizona.slice1 and plc.arizona.
-    
-        @param name human readable name to test  
-        """
-        object_hrn = self.object_gid.get_hrn()
-        if object_hrn == name:
-            return
-        if name.startswith(object_hrn + "."):
-            return
-        #if name.startswith(get_authority(name)):
-            #return
-    
-        raise PermissionError(name)
-
-    def determine_user_rights(self, caller_hrn, reg_record):
-        """
-        Given a user credential and a record, determine what set of rights the
-        user should have to that record.
-        
-        This is intended to replace determine_user_rights() and
-        verify_cancreate_credential()
-        """
-
-        rl = Rights()
-        type = reg_record.type
-
-        logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
-
-        if type == 'slice':
-            # researchers in the slice are in the DB as-is
-            researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
-            # locating PIs attached to that slice
-            slice_pis=reg_record.get_pis()
-            pi_hrns = [ user.hrn for user in slice_pis ]
-            if (caller_hrn in researcher_hrns + pi_hrns):
-                rl.add('refresh')
-                rl.add('embed')
-                rl.add('bind')
-                rl.add('control')
-                rl.add('info')
-
-        elif type == 'authority':
-            pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
-            if (caller_hrn == self.config.SFA_INTERFACE_HRN):
-                rl.add('authority')
-                rl.add('sa')
-                rl.add('ma')
-            if (caller_hrn in pi_hrns):
-                rl.add('authority')
-                rl.add('sa')
-            # NOTE: for the PL implementation, this 'operators' list 
-            # amounted to users with 'tech' role in that site 
-            # it seems like this is not needed any longer, so for now I just drop that
-            # operator_hrns = reg_record.get('operator',[])
-            # if (caller_hrn in operator_hrns):
-            #    rl.add('authority')
-            #    rl.add('ma')
-
-        elif type == 'user':
-            rl.add('refresh')
-            rl.add('resolve')
-            rl.add('info')
-
-        elif type == 'node':
-            rl.add('operator')
-
-        return rl
-
-    def get_authority(self, hrn):
-        return get_authority(hrn)
-
-    def filter_creds_by_caller(self, creds, caller_hrn_list):
-        """
-        Returns a list of creds who's gid caller matches the 
-        specified caller hrn
-        """
-        if not isinstance(creds, list):
-            creds = [creds]
-        creds = []
-        if not isinstance(caller_hrn_list, list):
-            caller_hrn_list = [caller_hrn_list]
-        for cred in creds:
-            try:
-                tmp_cred = Credential(string=cred)
-                if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]:
-                    creds.append(cred)
-            except: pass
-        return creds
-
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+  \r
+  Copyright (c) 2014 Raytheon BBN Technologies\r
\r
+  Permission is hereby granted, free of charge, to any person obtaining\r
+  a copy of this software and/or hardware specification (the "Work") to\r
+  deal in the Work without restriction, including without limitation the\r
+  rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+  and/or sell copies of the Work, and to permit persons to whom the Work\r
+  is furnished to do so, subject to the following conditions:\r
+\r
+  The above copyright notice and this permission notice shall be\r
+  included in all copies or substantial portions of the Work.\r
\r
+  THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
+  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
+  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+  OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS\r
+  IN THE WORK.\r
+\r
+  Portions have this copyright:\r
+\r
+  GENIPUBLIC-COPYRIGHT\r
+  Copyright (c) 2008-2009 University of Utah and the Flux Group.\r
+  All rights reserved.\r
+  \r
+-->\r
+<!--\r
+  GENI credential and privilege specification. The key points:\r
+  \r
+  * A credential is a set of privileges or a Ticket, each with a flag\r
+    to indicate delegation is permitted. Or an ABAC RT0 statement.\r
+  * A credential is signed and the signature included in the body of the\r
+    document.\r
+  * To support delegation, a credential will include its parent, and that\r
+    blob will be signed. So, there will be multiple signatures in the\r
+    document, each with a reference to the credential it signs.\r
+  \r
+  Default namespace = "http://www.geni.net/resources/credential/2"\r
+-->\r
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" xmlns:sig="http://www.w3.org/2000/09/xmldsig#">\r
+  <xs:include schemaLocation="protogeni-rspec-common.xsd"/>\r
+  <xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="sig.xsd"/>\r
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>\r
+  <xs:group name="anyelementbody">\r
+    <xs:sequence>\r
+      <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>\r
+    </xs:sequence>\r
+  </xs:group>\r
+  <xs:attributeGroup name="anyelementbody">\r
+    <xs:anyAttribute processContents="skip"/>\r
+  </xs:attributeGroup>\r
+  <!-- This is where we get the definition of RSpec from -->\r
+  <xs:element name="privilege">\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element ref="name"/>\r
+        <xs:element name="can_delegate" type="xs:boolean"/>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="name">\r
+    <xs:simpleType>\r
+      <xs:restriction base="xs:string">\r
+        <xs:minLength value="1"/>\r
+      </xs:restriction>\r
+    </xs:simpleType>\r
+  </xs:element>\r
+  <xs:element name="privileges"> <!-- For type 'privilege' only -->\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="privilege"/>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="capability">\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element ref="name"/>\r
+        <xs:element name="can_delegate">\r
+          <xs:simpleType>\r
+            <xs:restriction base="xs:token">\r
+              <xs:enumeration value="0"/>\r
+              <xs:enumeration value="1"/>\r
+            </xs:restriction>\r
+          </xs:simpleType>\r
+        </xs:element>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="capabilities"> <!-- For type 'capability' only -->\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="capability"/>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="ticket"> <!-- For type 'ticket' only -->\r
+    <xs:complexType mixed="true">\r
+      <xs:sequence>\r
+        <xs:element name="can_delegate" type="xs:boolean">\r
+          <xs:annotation>\r
+            <xs:documentation>Can the ticket be delegated?</xs:documentation>\r
+          </xs:annotation>\r
+        </xs:element>\r
+        <xs:element ref="redeem_before"/>\r
+        <xs:group ref="anyelementbody">\r
+          <xs:annotation>\r
+            <xs:documentation>A desciption of the resources that are being promised</xs:documentation>\r
+          </xs:annotation>\r
+        </xs:group>\r
+      </xs:sequence>\r
+      <xs:attributeGroup ref="anyelementbody"/>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="redeem_before" type="xs:dateTime">\r
+    <xs:annotation>\r
+      <xs:documentation>The ticket must be "cashed in" by this date </xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+\r
+  <!-- Elements used for type 'abac'. See http://groups.geni.net/geni/wiki/TIEDABACCredential -->\r
+  <xs:element name="ABACprincipal">\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+       <xs:element name="keyid" type="xs:string"/> <!-- SHA1 hash of the principal's public key -->\r
+       <xs:element name="mnemonic" type="xs:string" minOccurs="0" maxOccurs="1"/> <!-- EG principal's URN -->\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <!-- A single rt0 element is required for creds of type 'abac'. Must have a single 'head'\r
+       and at least one 'tail'. -->\r
+  <xs:element name="rt0">\r
+    <xs:annotation>\r
+      <xs:documentation>An ABAC RT0 statement, used only for type 'abac'.</xs:documentation>\r
+    </xs:annotation>\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+       <xs:element name="version" type="xs:string" /> <!-- 1.1 for this schema -->\r
+       <xs:element name="head">\r
+         <xs:complexType>\r
+           <xs:sequence>\r
+             <xs:element ref="ABACprincipal"/> <!-- Matching the cred signer -->\r
+             <xs:element name="role" type="xs:string"/>\r
+           </xs:sequence>\r
+         </xs:complexType>\r
+       </xs:element>\r
+       <xs:element name="tail" minOccurs="1" maxOccurs="unbounded">\r
+         <xs:complexType>\r
+           <xs:sequence>\r
+             <xs:element ref="ABACprincipal"/>\r
+             <xs:element name="role" type="xs:string" minOccurs="0" maxOccurs="1"/>\r
+             <xs:element name="linking_role" type="xs:string" minOccurs="0" \r
+                         maxOccurs="1"/>\r
+           </xs:sequence>\r
+         </xs:complexType>\r
+       </xs:element>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="abac">\r
+    <xs:annotation>\r
+      <xs:documentation>An ABAC assertion containing a single RT0 statement, used only for type 'abac'.</xs:documentation>\r
+    </xs:annotation>\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+       <xs:element minOccurs="1" maxOccurs="1" ref="rt0"/>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+\r
+  <xs:element name="signatures">\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element maxOccurs="unbounded" ref="sig:Signature"/>\r
+      </xs:sequence>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:complexType name="credentials">\r
+    <xs:annotation>\r
+      <xs:documentation>A credential granting privileges or a ticket or making an ABAC assertion.</xs:documentation>\r
+    </xs:annotation>\r
+    <xs:sequence>\r
+      <xs:element ref="credential"/>\r
+    </xs:sequence>\r
+  </xs:complexType>\r
+  <xs:element name="credential">\r
+    <xs:complexType>\r
+      <xs:sequence>\r
+        <xs:element ref="type"/>\r
+        <xs:element ref="serial"/>\r
+        <xs:element ref="owner_gid"/>\r
+        <xs:element minOccurs="0" ref="owner_urn"/>\r
+        <xs:element ref="target_gid"/>\r
+        <xs:element minOccurs="0" ref="target_urn"/>\r
+        <xs:element ref="uuid"/>\r
+        <xs:element ref="expires"/>\r
+        <xs:choice>\r
+          <xs:annotation>\r
+            <xs:documentation>Privileges or a ticket or an ABAC assertion</xs:documentation>\r
+          </xs:annotation>\r
+          <xs:element ref="privileges"/>\r
+          <xs:element ref="ticket"/>\r
+          <xs:element ref="capabilities"/>\r
+         <xs:element ref="abac"/>\r
+        </xs:choice>\r
+        <xs:element minOccurs="0" maxOccurs="unbounded" ref="extensions"/>\r
+        <xs:element minOccurs="0" ref="parent"/>\r
+      </xs:sequence>\r
+      <xs:attribute ref="xml:id" use="required"/>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="type">\r
+    <xs:annotation>\r
+      <xs:documentation>The type of this credential. Currently a Privilege set or a Ticket or ABAC.</xs:documentation>\r
+    </xs:annotation>\r
+    <xs:simpleType>\r
+      <xs:restriction base="xs:token">\r
+        <xs:enumeration value="privilege"/>\r
+        <xs:enumeration value="ticket"/>\r
+        <xs:enumeration value="capability"/>\r
+        <xs:enumeration value="abac"/>\r
+      </xs:restriction>\r
+    </xs:simpleType>\r
+  </xs:element>\r
+  <xs:element name="serial" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>A serial number.</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="owner_gid" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>GID of the owner of this credential. </xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="owner_urn" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>URN of the owner. Not everyone can parse DER</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="target_gid" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>GID of the target of this credential. </xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="target_urn" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>URN of the target.</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="uuid" type="xs:string">\r
+    <xs:annotation>\r
+      <xs:documentation>UUID of this credential</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="expires" type="xs:dateTime">\r
+    <xs:annotation>\r
+      <xs:documentation>Expires on in ISO8601 format but preferably RFC3339</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="extensions">\r
+    <xs:annotation>\r
+      <xs:documentation>Optional Extensions</xs:documentation>\r
+    </xs:annotation>\r
+    <xs:complexType mixed="true">\r
+      <xs:group ref="anyelementbody"/>\r
+      <xs:attributeGroup ref="anyelementbody"/>\r
+    </xs:complexType>\r
+  </xs:element>\r
+  <xs:element name="parent" type="credentials">\r
+    <xs:annotation>\r
+      <xs:documentation>Parent that delegated to us</xs:documentation>\r
+    </xs:annotation>\r
+  </xs:element>\r
+  <xs:element name="signed-credential">\r
+    <xs:complexType>\r
+      <xs:complexContent>\r
+        <xs:extension base="credentials">\r
+          <xs:sequence>\r
+            <xs:element minOccurs="0" ref="signatures"/>\r
+          </xs:sequence>\r
+        </xs:extension>\r
+      </xs:complexContent>\r
+    </xs:complexType>\r
+  </xs:element>\r
+</xs:schema>\r