# 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
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
-# 2 weeks, in seconds
-DEFAULT_CREDENTIAL_LIFETIME = 86400 * 31
+# 31 days, in seconds
+DEFAULT_CREDENTIAL_LIFETIME = 86400 * 28
# TODO:
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])
- break
+ return
else:
self.xml = str
self.decode()
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:
# 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)
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)
# 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)
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:
# 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
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 = ""
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)
# 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()))
##
# 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:
# 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
##
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: