+ # Note: you could also not copy attributes
+ # which already exist. It appears that both PG and PL
+ # will actually validate a slicecred with a parent
+ # signed using PG namespaces and a child signed with PL
+ # namespaces over the whole thing. But I don't know
+ # if that is a bug in xmlsec1, an accident since
+ # the contents of the schemas are the same,
+ # or something else, but it seems odd. And this works.
+ parentRoot = sdoc.documentElement
+ if parentRoot.tagName == "signed-credential" and parentRoot.hasAttributes():
+ for attrIx in range(0, parentRoot.attributes.length):
+ attr = parentRoot.attributes.item(attrIx)
+ # returns the old attribute of same name that was
+ # on the credential
+ # Below throws InUse exception if we forgot to clone the attribute first
+ oldAttr = signed_cred.setAttributeNode(attr.cloneNode(True))
+ if oldAttr and oldAttr.value != attr.value:
+ msg = "Delegating cred from owner %s to %s over %s replaced attribute %s value '%s' with '%s'" % (self.parent.gidCaller.get_urn(), self.gidCaller.get_urn(), self.gidObject.get_urn(), oldAttr.name, oldAttr.value, attr.value)
+ logger.warn(msg)
+ #raise CredentialNotVerifiable("Can't encode new valid delegated credential: %s" % msg)
+
+ p_cred = doc.importNode(sdoc.getElementsByTagName("credential")[0], True)
+ p = doc.createElement("parent")
+ p.appendChild(p_cred)
+ cred.appendChild(p)
+ # done handling parent credential
+
+ # Create the <signatures> tag
+ signatures = doc.createElement("signatures")
+ signed_cred.appendChild(signatures)
+
+ # Add any parent signatures
+ if self.parent:
+ for cur_cred in self.get_credential_list()[1:]:
+ sdoc = parseString(cur_cred.get_signature().get_xml())
+ ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
+ signatures.appendChild(ele)
+
+ # Get the finished product
+ self.xml = doc.toxml()
+
+
+ def save_to_random_tmp_file(self):
+ fp, filename = mkstemp(suffix='cred', text=True)
+ fp = os.fdopen(fp, "w")
+ self.save_to_file(filename, save_parents=True, filep=fp)
+ return filename
+
+ def save_to_file(self, filename, save_parents=True, filep=None):
+ if not self.xml:
+ self.encode()
+ if filep:
+ f = filep
+ else:
+ f = open(filename, "w")
+ f.write(self.xml)
+ f.close()
+
+ def save_to_string(self, save_parents=True):
+ if not self.xml:
+ self.encode()
+ return self.xml
+
+ def get_refid(self):
+ if not self.refid:
+ self.refid = 'ref0'
+ return self.refid
+
+ def set_refid(self, rid):
+ self.refid = rid
+
+ ##
+ # Figure out what refids exist, and update this credential's id
+ # so that it doesn't clobber the others. Returns the refids of
+ # the parents.
+
+ def updateRefID(self):
+ if not self.parent:
+ self.set_refid('ref0')
+ return []
+
+ refs = []
+
+ next_cred = self.parent
+ while next_cred:
+ refs.append(next_cred.get_refid())
+ if next_cred.parent:
+ next_cred = next_cred.parent
+ else:
+ next_cred = None
+
+
+ # Find a unique refid for this credential
+ rid = self.get_refid()
+ while rid in refs:
+ val = int(rid[3:])
+ rid = "ref%d" % (val + 1)
+
+ # Set the new refid
+ self.set_refid(rid)
+
+ # Return the set of parent credential ref ids
+ return refs
+
+ def get_xml(self):
+ if not self.xml:
+ self.encode()
+ return self.xml
+
+ ##
+ # Sign the XML file created by encode()
+ #
+ # WARNING:
+ # In general, a signed credential obtained externally should
+ # not be changed else the signature is no longer valid. So, once
+ # you have loaded an existing signed credential, do not call encode() or sign() on it.
+
+ def sign(self):
+ if not self.issuer_privkey or not self.issuer_gid:
+ return
+ doc = parseString(self.get_xml())
+ sigs = doc.getElementsByTagName("signatures")[0]
+
+ # Create the signature template to be signed
+ signature = Signature()
+ signature.set_refid(self.get_refid())
+ sdoc = parseString(signature.get_xml())
+ sig_ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
+ sigs.appendChild(sig_ele)
+
+ self.xml = doc.toxml()
+
+
+ # Split the issuer GID into multiple certificates if it's a chain
+ chain = GID(filename=self.issuer_gid)
+ gid_files = []
+ while chain:
+ gid_files.append(chain.save_to_random_tmp_file(False))
+ if chain.get_parent():
+ chain = chain.get_parent()
+ else:
+ chain = None
+
+
+ # Call out to xmlsec1 to sign it
+ ref = 'Sig_%s' % self.get_refid()
+ filename = self.save_to_random_tmp_file()
+ signed = os.popen('%s --sign --node-id "%s" --privkey-pem %s,%s %s' \
+ % (self.xmlsec_path, ref, self.issuer_privkey, ",".join(gid_files), filename)).read()
+ os.remove(filename)
+
+ for gid_file in gid_files:
+ os.remove(gid_file)
+
+ self.xml = signed
+
+ # This is no longer a legacy credential
+ if self.legacy:
+ self.legacy = None
+
+ # Update signatures
+ self.decode()
+
+