X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Ftrust%2Fcredential.py;h=cda6a09af69df899436312753ac74e36ca11e24a;hb=27e30f7854884928bd4850afa3c6ce5c7f93f7f4;hp=775cde96696fc738198a7fcbf3fab48b4813149c;hpb=6617f963eb444c8520bc6b7941ef7358c16fb3d5;p=sfa.git diff --git a/sfa/trust/credential.py b/sfa/trust/credential.py index 775cde96..cda6a09a 100644 --- a/sfa/trust/credential.py +++ b/sfa/trust/credential.py @@ -26,7 +26,8 @@ # Credentials are signed XML files that assign a subject gid privileges to an object gid ## -import os +import os, os.path +import subprocess from types import StringTypes import datetime from StringIO import StringIO @@ -44,13 +45,13 @@ from xml.parsers.expat import ExpatError from sfa.util.faults import CredentialNotVerifiable, ChildRightsNotSubsetOfParent from sfa.util.sfalogging import logger -from sfa.util.sfatime import utcparse +from sfa.util.sfatime import utcparse, SFATIME_FORMAT from sfa.trust.rights import Right, Rights, determine_rights from sfa.trust.gid import GID from sfa.util.xrn import urn_to_hrn, hrn_authfor_hrn # 31 days, in seconds -DEFAULT_CREDENTIAL_LIFETIME = 86400 * 31 +DEFAULT_CREDENTIAL_LIFETIME = 86400 * 28 # TODO: @@ -268,7 +269,7 @@ class Credential(object): str = file(filename).read() # if this is a legacy credential, write error and bail out - if str.strip().startswith("-----"): + if isinstance (str, StringTypes) and str.strip().startswith("-----"): logger.error("Legacy credentials not supported any more - giving up with %s..."%str[:10]) return else: @@ -292,14 +293,14 @@ class Credential(object): return subject # sounds like this should be __repr__ instead ?? - def get_summary_tostring(self): + def pretty_cred(self): if not self.gidObject: self.decode() obj = self.gidObject.get_printable_subject() caller = self.gidCaller.get_printable_subject() exp = self.get_expiration() # Summarize the rights too? The issuer? - return "[ Grant %s rights on %s until %s ]" % (caller, obj, exp) + return "[ Grant {caller} rights on {obj} until {exp} ]".format(**locals()) def get_signature(self): if not self.signature: @@ -364,15 +365,11 @@ class Credential(object): # Expiration: an absolute UTC time of expiration (as either an int or string or datetime) # def set_expiration(self, expiration): - if isinstance(expiration, (int, float)): - self.expiration = datetime.datetime.fromtimestamp(expiration) - elif isinstance (expiration, datetime.datetime): - self.expiration = expiration - elif isinstance (expiration, StringTypes): - self.expiration = utcparse (expiration) + expiration_datetime = utcparse (expiration) + if expiration_datetime is not None: + self.expiration = expiration_datetime else: - logger.error ("unexpected input type in Credential.set_expiration") - + logger.error ("unexpected input %s in Credential.set_expiration"%expiration) ## # get the lifetime of the credential (always in datetime format) @@ -458,9 +455,10 @@ class Credential(object): append_sub(doc, cred, "target_urn", self.gidObject.get_urn()) append_sub(doc, cred, "uuid", "") if not self.expiration: + logger.debug("Creating credential valid for %s s"%DEFAULT_CREDENTIAL_LIFETIME) self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) self.expiration = self.expiration.replace(microsecond=0) - append_sub(doc, cred, "expires", self.expiration.isoformat()) + append_sub(doc, cred, "expires", self.expiration.strftime(SFATIME_FORMAT)) privileges = doc.createElement("privileges") cred.appendChild(privileges) @@ -515,7 +513,8 @@ class Credential(object): # 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:\n - 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) + msg = "Delegating cred from owner %s to %s over %s:\n - 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) @@ -777,7 +776,7 @@ class Credential(object): xmlschema = etree.XMLSchema(schema_doc) if not xmlschema.validate(tree): error = xmlschema.error_log.last_error - message = "%s: %s (line %s)" % (self.get_summary_tostring(), error.message, error.line) + message = "%s: %s (line %s)" % (self.pretty_cred(), error.message, error.line) raise CredentialNotVerifiable(message) if trusted_certs_required and trusted_certs is None: @@ -801,12 +800,12 @@ class Credential(object): # make sure it is not expired if self.get_expiration() < datetime.datetime.utcnow(): - raise CredentialNotVerifiable("Credential %s expired at %s" % (self.get_summary_tostring(), self.expiration.isoformat())) + raise CredentialNotVerifiable("Credential %s expired at %s" % \ + (self.pretty_cred(), + self.expiration.strftime(SFATIME_FORMAT))) # Verify the signatures filename = self.save_to_random_tmp_file() - if trusted_certs is not None: - cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs]) # If caller explicitly passed in None that means skip cert chain validation. # - Strange and not typical @@ -829,11 +828,25 @@ class Credential(object): if trusted_certs is None: break -# print "Doing %s --verify --node-id '%s' %s %s 2>&1" % \ -# (self.xmlsec_path, ref, cert_args, filename) - verified = os.popen('%s --verify --node-id "%s" %s %s 2>&1' \ - % (self.xmlsec_path, ref, cert_args, filename)).read() - if not verified.strip().startswith("OK"): + # Thierry - jan 2015 + # up to fedora20 we used os.popen and checked that the output begins with OK + # turns out, with fedora21, there is extra input before this 'OK' thing + # looks like we're better off just using the exit code - that's what it is made for + #cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs]) + #command = '{} --verify --node-id "{}" {} {} 2>&1'.\ + # format(self.xmlsec_path, ref, cert_args, filename) + command = [ self.xmlsec_path, '--verify', '--node-id', ref ] + for trusted in trusted_certs: + command += ["--trusted-pem", trusted ] + command += [ filename ] + logger.debug("Running " + " ".join(command)) + try: + verified = subprocess.check_output(command, stderr=subprocess.STDOUT) + logger.debug("xmlsec command returned {}".format(verified)) + if "OK\n" not in verified: + logger.warning("WARNING: xmlsec1 seemed to return fine but without a OK in its output") + except subprocess.CalledProcessError as e: + verified = e.output # xmlsec errors have a msg= which is the interesting bit. mstart = verified.find("msg=") msg = "" @@ -841,7 +854,9 @@ class Credential(object): mstart = mstart + 4 mend = verified.find('\\', mstart) msg = verified[mstart:mend] - raise CredentialNotVerifiable("xmlsec1 error verifying cred %s using Signature ID %s: %s %s" % (self.get_summary_tostring(), ref, msg, verified.strip())) + logger.warning("Credential.verify - failed - xmlsec1 returned {}".format(verified.strip())) + raise CredentialNotVerifiable("xmlsec1 error verifying cred %s using Signature ID %s: %s" % \ + (self.pretty_cred(), ref, msg)) os.remove(filename) # Verify the parents (delegation) @@ -939,7 +954,8 @@ class Credential(object): # Give up, credential does not pass issuer verification - raise CredentialNotVerifiable("Could not verify credential owned by %s for object %s. Cred signer %s not the trusted authority for Cred target %s" % (self.gidCaller.get_urn(), self.gidObject.get_urn(), root_cred_signer.get_hrn(), root_target_gid.get_hrn())) + raise CredentialNotVerifiable("Could not verify credential owned by %s for object %s. Cred signer %s not the trusted authority for Cred target %s" % \ + (self.gidCaller.get_urn(), self.gidObject.get_urn(), root_cred_signer.get_hrn(), root_target_gid.get_hrn())) ## @@ -954,22 +970,26 @@ class Credential(object): # parents rights (and check delegate bits) if not parent_cred.get_privileges().is_superset(self.get_privileges()): raise ChildRightsNotSubsetOfParent(("Parent cred ref %s rights " % parent_cred.get_refid()) + - self.parent.get_privileges().save_to_string() + (" not superset of delegated cred %s ref %s rights " % (self.get_summary_tostring(), self.get_refid())) + + self.parent.get_privileges().save_to_string() + (" not superset of delegated cred %s ref %s rights " % \ + (self.get_summary_tostring(), self.get_refid())) + self.get_privileges().save_to_string()) # make sure my target gid is the same as the parent's if not parent_cred.get_gid_object().save_to_string() == \ self.get_gid_object().save_to_string(): - raise CredentialNotVerifiable("Delegated cred %s: Target gid not equal between parent and child. Parent %s" % (self.get_summary_tostring(), parent_cred.get_summary_tostring())) + raise CredentialNotVerifiable("Delegated cred %s: Target gid not equal between parent and child. Parent %s" % \ + (self.get_summary_tostring(), parent_cred.get_summary_tostring())) # make sure my expiry time is <= my parent's if not parent_cred.get_expiration() >= self.get_expiration(): - raise CredentialNotVerifiable("Delegated credential %s expires after parent %s" % (self.get_summary_tostring(), parent_cred.get_summary_tostring())) + raise CredentialNotVerifiable("Delegated credential %s expires after parent %s" % \ + (self.pretty_cred(), parent_cred.pretty_cred())) # make sure my signer is the parent's caller if not parent_cred.get_gid_caller().save_to_string(False) == \ self.get_signature().get_issuer_gid().save_to_string(False): - raise CredentialNotVerifiable("Delegated credential %s not signed by parent %s's caller" % (self.get_summary_tostring(), parent_cred.get_summary_tostring())) + raise CredentialNotVerifiable("Delegated credential %s not signed by parent %s's caller" % \ + (self.pretty_cred(), parent_cred.pretty_cred())) # Recurse if parent_cred.parent: @@ -1032,7 +1052,8 @@ class Credential(object): # else this looks like a delegated credential, and the real caller is the issuer else: actual_caller_hrn=issuer_hrn - logger.info("actual_caller_hrn: caller_hrn=%s, issuer_hrn=%s, returning %s"%(caller_hrn,issuer_hrn,actual_caller_hrn)) + logger.info("actual_caller_hrn: caller_hrn=%s, issuer_hrn=%s, returning %s" + %(caller_hrn,issuer_hrn,actual_caller_hrn)) return actual_caller_hrn ## @@ -1063,7 +1084,7 @@ class Credential(object): self.get_signature().get_issuer_gid().dump(8, dump_parents) if self.expiration: - print " expiration:", self.expiration.isoformat() + print " expiration:", self.expiration.strftime(SFATIME_FORMAT) gidObject = self.get_gid_object() if gidObject: