diff --git a/requirements.txt b/requirements.txt index 11d2c1b..8c7fbc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ Werkzeug==0.9.4 argparse==1.2.1 blinker==1.3 cffi==0.8.1 -cryptography==0.2.2 +cryptography==0.6.1 flup==1.0.2 itsdangerous==0.23 jsonrpclib==0.1.3 diff --git a/src/plugins/fedtools/delegatetools.py b/src/plugins/fedtools/delegatetools.py index 84b5986..b262fa4 100644 --- a/src/plugins/fedtools/delegatetools.py +++ b/src/plugins/fedtools/delegatetools.py @@ -308,7 +308,12 @@ def check_if_authorized(self, credentials, owner_cert, method, type_, target_urn if not required_privileges or set(privileges+priv_from_cred).intersection(required_privileges): cred_accepted = True break +#This would be cleaner, but I don't know what the correct way to import this is +# except Credential.CredentialExpireTimezoneError as e: +# raise GFedv2AuthorizationError("Your credentials has an invalid expire date (missing timezone info)") except Exception as e: + if (e.args[0].startswith('Invalid expire value in credential, missing timezone info')): + raise GFedv2AuthorizationError("Your credentials has an invalid expire date (missing timezone info)") import traceback traceback.print_exc() #print e @@ -1034,4 +1039,4 @@ def check_list_of_dictionaries(value): raise Exception for dictionary in value: if not isinstance(dictionary, dict): - raise Exception \ No newline at end of file + raise Exception diff --git a/src/plugins/osliceauthorityrm/osliceauthorityresourcemanager.py b/src/plugins/osliceauthorityrm/osliceauthorityresourcemanager.py index fafd42f..3c88f39 100644 --- a/src/plugins/osliceauthorityrm/osliceauthorityresourcemanager.py +++ b/src/plugins/osliceauthorityrm/osliceauthorityresourcemanager.py @@ -17,7 +17,7 @@ class OSliceAuthorityResourceManager(object): SA_CERT_FILE = 'sa-cert.pem' SA_KEY_FILE = 'sa-key.pem' - CRED_EXPIRY = datetime.datetime.utcnow() + datetime.timedelta(days=600) + CRED_EXPIRY = (datetime.datetime.utcnow() + datetime.timedelta(days=600)).replace(tzinfo=pytz.utc) AUTHORITY_NAME = 'sa' #: The short-name for this authority SUPPORTED_SERVICES = ['SLICE', 'SLICE_MEMBER', 'SLIVER_INFO', 'PROJECT', 'PROJECT_MEMBER'] #: The objects supported by this authority @@ -154,12 +154,14 @@ def create_slice(self, credentials, fields, options): fields['SLICE_CREATION'] = pyrfc3339.generate(datetime.datetime.utcnow().replace(tzinfo=pytz.utc)) fields['SLICE_EXPIRED'] = False + assert self.CRED_EXPIRY.tzinfo is not None + assert self.CRED_EXPIRY.utcoffset() is not None if 'SLICE_EXPIRATION' in fields: - expDate = datetime.datetime.strptime(fields['SLICE_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ') + expDate = datetime.datetime.strptime(fields['SLICE_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc) if expDate > self.CRED_EXPIRY: - fields['SLICE_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY.replace(tzinfo=pytz.utc)) + fields['SLICE_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY) else: - fields['SLICE_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY.replace(tzinfo=pytz.utc)) + fields['SLICE_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY) # Generating Slice certificate s_cert, s_pu, s_pr = geniutil.create_certificate(slice_urn, self._sa_pr, self._sa_c, life_days=3650) @@ -268,12 +270,14 @@ def create_project(self, credentials, fields, options): # Verify the uniqueness of project name self._validate_project_urn(p_urn) + assert self.CRED_EXPIRY.tzinfo is not None + assert self.CRED_EXPIRY.utcoffset() is not None if 'PROJECT_EXPIRATION' in fields: - expDate = datetime.datetime.strptime(fields['PROJECT_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ') + expDate = datetime.datetime.strptime(fields['PROJECT_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc) if expDate > self.CRED_EXPIRY: - fields['PROJECT_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY.replace(tzinfo=pytz.utc)) + fields['PROJECT_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY) else: - fields['PROJECT_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY.replace(tzinfo=pytz.utc)) + fields['PROJECT_EXPIRATION'] = pyrfc3339.generate(self.CRED_EXPIRY) geniutil = pm.getService('geniutil') # Generating Project Certificate @@ -410,6 +414,8 @@ def modify_slice_membership(self, urn, credentials, options): if user_urn_from_cert != slice_lead and user_urn_from_cert != target_urn_from_cred: raise self.gfed_ex.GFedv2ArgumentError("Only slice LEAD or ROOT can modify lead membership "+ str(urn)) + assert self.CRED_EXPIRY.tzinfo is not None + assert self.CRED_EXPIRY.utcoffset() is not None # Create slice cred for new member if option_key == 'members_to_add': member_dict['SLICE_CREDENTIALS'] = geniutil.create_credential_ex(owner_cert=member_cert, target_cert=slice_cert, @@ -472,6 +478,8 @@ def _update_cred_expiry(self, cred, obj_cert, expiry): :param expiry: :return: """ + assert expiry.tzinfo is not None + assert expiry.utcoffset() is not None geniutil = pm.getService('geniutil') owner_cert = geniutil.extract_owner_certificate(cred) priv, _ = geniutil.get_privileges_and_target_urn(cred) @@ -575,6 +583,8 @@ def update_slice_credentials_for_member(self, member_urn, credentials, options): slice_creds_old = membership['SLICE_CREDENTIALS'] slice_prvlg, slice_urn = geniutil.get_privileges_and_target_urn(slice_creds_old) slice_exp = geniutil.get_expiration(slice_creds_old) + assert slice_exp.tzinfo is not None + assert slice_exp.utcoffset() is not None slice_creds_new = geniutil.create_credential_ex(owner_cert=member_cert, target_cert=slice_cert, issuer_key=self._sa_pr, issuer_cert=self._sa_c, privileges_list=slice_prvlg, expiration=slice_exp) @@ -654,7 +664,7 @@ def _validate_slice_urn(self, slice_urn): if len(lookup_results) > 0: # Slice exists, Let's check if it has been expired - expDate = datetime.datetime.strptime(lookup_results[0]['SLICE_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ') + expDate = datetime.datetime.strptime(lookup_results[0]['SLICE_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc) now = datetime.datetime.utcnow() if now < expDate: raise self.gfed_ex.GFedv2DuplicateError("A slice with specified name already exists under the following URN:"+ str(slice_urn)) @@ -670,7 +680,7 @@ def _validate_project_urn(self, project_urn): if len(lookup_results) > 0: # Slice exists, Let's check if it has been expired - expDate = datetime.datetime.strptime(lookup_results[0]['PROJECT_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ') + expDate = datetime.datetime.strptime(lookup_results[0]['PROJECT_EXPIRATION'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc) now = datetime.datetime.utcnow() if now < expDate: raise self.gfed_ex.GFedv2DuplicateError("A project with specified name already exists under the following URN:"+ str(project_urn)) diff --git a/src/vendor/geni_trust/ext/sfa/trust/credential.py b/src/vendor/geni_trust/ext/sfa/trust/credential.py index 2a22994..9e20654 100644 --- a/src/vendor/geni_trust/ext/sfa/trust/credential.py +++ b/src/vendor/geni_trust/ext/sfa/trust/credential.py @@ -32,6 +32,9 @@ from StringIO import StringIO from tempfile import mkstemp from xml.dom.minidom import Document, parseString +import pytz +import pyrfc3339 +import dateutil HAVELXML = False try: @@ -227,6 +230,11 @@ def filter_creds_by_caller(creds, caller_hrn_list): except: pass return caller_creds +class ExpireTimezoneError(ValueError): + pass +class CredentialExpireTimezoneError(ValueError): + pass + class Credential(object): ## @@ -310,7 +318,7 @@ def translate_legacy(self, str): self.gidObject = legacy.get_gid_object() lifetime = legacy.get_lifetime() if not lifetime: - self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) + self.set_expiration((datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)).replace(tzinfo=pytz.utc)) else: self.set_expiration(int(lifetime)) self.lifeTime = legacy.get_lifetime() @@ -372,13 +380,30 @@ def get_gid_object(self): # def set_expiration(self, expiration): if isinstance(expiration, (int, float)): - self.expiration = datetime.datetime.fromtimestamp(expiration) + self.expiration = datetime.datetime.utcfromtimestamp(expiration).replace(tzinfo=pytz.utc) elif isinstance (expiration, datetime.datetime): self.expiration = expiration + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + raise ValueError('set_expiration called with expire time missing timezone info') elif isinstance (expiration, StringTypes): - self.expiration = utcparse (expiration) + self.expiration = dateutil.parser.parse(expiration) + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + raise ExpireTimezoneError('set_expiration callled with String expire time missing timezone info: "{0}"'.format(expiration)) else: logger.error ("unexpected input type in Credential.set_expiration") + raise ValueError('unexpected input type in Credential.set_expiration', expiration) + + if not isinstance (self.expiration, datetime.datetime): + logger.error('credential.py bug: set_expiration finished with credential is not datetime. It is {0}'.format(self.expiration.__class__)) + raise RuntimeError('credential.py bug: set_expiration finished with credential that is not datetime') + + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + logger.error('credential.py bug: set_expiration finished with credential that has expire time missing timezone info') + raise RuntimeError('credential.py bug: set_expiration finished with credential that has expire time missing timezone info') + + if t.utcoffset() is not None: + #force timezone to UTC without changing time + t = t.astimezone(pytz.utc) ## @@ -469,10 +494,21 @@ def encode(self): append_sub(doc, cred, "target_gid", self.gidObject.save_to_string()) append_sub(doc, cred, "target_urn", self.gidObject.get_urn()) append_sub(doc, cred, "uuid", "") + + #was: if not self.expiration: + #was: self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) if not self.expiration: - 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()) + logger.error('credential.py usage bug: credential was created without expire') + raise RuntimeError('credential.py usage bug: credential was created without expire') + if (not isinstance (self.expiration, datetime.datetime)) or self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + #credential.py bug because set_expiration should have handled this + logger.error('credential.py bug: credential was has expire time missing timezone info') + raise RuntimeError('credential.py bug: credential was has expire time missing timezone info') + else: + self.expiration = self.expiration.replace(microsecond=0) + append_sub(doc, cred, "expires", pyrfc3339.generate(self.expiration)) + #was: append_sub(doc, cred, "expires", self.expiration.isoformat()) + privileges = doc.createElement("privileges") cred.appendChild(privileges) @@ -709,7 +745,10 @@ def decode(self): cred = creds[0] self.set_refid(cred.getAttribute("xml:id")) - self.set_expiration(utcparse(getTextNode(cred, "expires"))) + try: + self.set_expiration(getTextNode(cred, "expires")) + except ExpireTimezoneError as e: + raise CredentialExpireTimezoneError('Invalid expire value in credential, missing timezone info: "{0}"'.format(getTextNode(cred, "expires"))) self.gidCaller = GID(string=getTextNode(cred, "owner_gid")) self.gidObject = GID(string=getTextNode(cred, "target_gid")) diff --git a/src/vendor/geni_trust/ext/sfa/util/sfatime.py b/src/vendor/geni_trust/ext/sfa/util/sfatime.py index 15de02f..29e6502 100644 --- a/src/vendor/geni_trust/ext/sfa/util/sfatime.py +++ b/src/vendor/geni_trust/ext/sfa/util/sfatime.py @@ -50,6 +50,9 @@ def utcparse(input): t = dateutil.parser.parse(input) if t.utcoffset() is not None: t = t.utcoffset() + t.replace(tzinfo=None) + # the above line is wrong: you need to substract the utcoffset, not add it! + # a better way is: t = t.astimezone(pytz.utc) #only works if not naive + raise RuntimeException('unfixed bug in code triggered') return t elif isinstance (input, (int,float,long)): return datetime.datetime.fromtimestamp(input) diff --git a/src/vendor/geniv3rpc/ext/sfa/trust/credential.py b/src/vendor/geniv3rpc/ext/sfa/trust/credential.py index 3b3cafc..a2d8e4a 100644 --- a/src/vendor/geniv3rpc/ext/sfa/trust/credential.py +++ b/src/vendor/geniv3rpc/ext/sfa/trust/credential.py @@ -32,6 +32,9 @@ from StringIO import StringIO from tempfile import mkstemp from xml.dom.minidom import Document, parseString +import pytz +import pyrfc3339 +import dateutil HAVELXML = False try: @@ -227,6 +230,11 @@ def filter_creds_by_caller(creds, caller_hrn_list): except: pass return caller_creds +class ExpireTimezoneError(ValueError): + pass +class CredentialExpireTimezoneError(ValueError): + pass + class Credential(object): ## @@ -310,7 +318,7 @@ def translate_legacy(self, str): self.gidObject = legacy.get_gid_object() lifetime = legacy.get_lifetime() if not lifetime: - self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) + self.set_expiration((datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)).replace(tzinfo=pytz.utc)) else: self.set_expiration(int(lifetime)) self.lifeTime = legacy.get_lifetime() @@ -372,13 +380,30 @@ def get_gid_object(self): # def set_expiration(self, expiration): if isinstance(expiration, (int, float)): - self.expiration = datetime.datetime.fromtimestamp(expiration) + self.expiration = datetime.datetime.utcfromtimestamp(expiration).replace(tzinfo=pytz.utc) elif isinstance (expiration, datetime.datetime): self.expiration = expiration + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + raise ValueError('set_expiration called with expire time missing timezone info') elif isinstance (expiration, StringTypes): - self.expiration = utcparse (expiration) + self.expiration = dateutil.parser.parse(expiration) + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + raise ExpireTimezoneError('set_expiration callled with String expire time missing timezone info: "{0}"'.format(expiration)) else: logger.error ("unexpected input type in Credential.set_expiration") + raise ValueError('unexpected input type in Credential.set_expiration', expiration) + + if not isinstance (self.expiration, datetime.datetime): + logger.error('credential.py bug: set_expiration finished with credential is not datetime. It is {0}'.format(self.expiration.__class__)) + raise RuntimeError('credential.py bug: set_expiration finished with credential that is not datetime') + + if self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + logger.error('credential.py bug: set_expiration finished with credential that has expire time missing timezone info') + raise RuntimeError('credential.py bug: set_expiration finished with credential that has expire time missing timezone info') + + if t.utcoffset() is not None: + #force timezone to UTC without changing time + t = t.astimezone(pytz.utc) ## @@ -469,10 +494,21 @@ def encode(self): append_sub(doc, cred, "target_gid", self.gidObject.save_to_string()) append_sub(doc, cred, "target_urn", self.gidObject.get_urn()) append_sub(doc, cred, "uuid", "") + + #was: if not self.expiration: + #was: self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) if not self.expiration: - self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME)) + logger.error('credential.py usage bug: credential was created without expire') + raise RuntimeError('credential.py usage bug: credential was created without expire') + if (not isinstance (self.expiration, datetime.datetime)) or self.expiration.tzinfo is None or self.expiration.tzinfo.utcoffset(self.expiration) is None: + #credential.py bug because set_expiration should have handled this + logger.error('credential.py bug: credential was has expire time missing timezone info') + raise RuntimeError('credential.py bug: credential was has expire time missing timezone info') + else: self.expiration = self.expiration.replace(microsecond=0) - append_sub(doc, cred, "expires", self.expiration.isoformat()) + append_sub(doc, cred, "expires", pyrfc3339.generate(self.expiration)) + #was: append_sub(doc, cred, "expires", self.expiration.isoformat()) + privileges = doc.createElement("privileges") cred.appendChild(privileges) @@ -707,7 +743,10 @@ def decode(self): cred = creds[0] self.set_refid(cred.getAttribute("xml:id")) - self.set_expiration(utcparse(getTextNode(cred, "expires"))) + try: + self.set_expiration(getTextNode(cred, "expires")) + except ExpireTimezoneError as e: + raise CredentialExpireTimezoneError('Invalid expire value in credential, missing timezone info: "{0}"'.format(getTextNode(cred, "expires"))) self.gidCaller = GID(string=getTextNode(cred, "owner_gid")) self.gidObject = GID(string=getTextNode(cred, "target_gid")) diff --git a/src/vendor/geniv3rpc/ext/sfa/util/sfatime.py b/src/vendor/geniv3rpc/ext/sfa/util/sfatime.py index 15de02f..29e6502 100644 --- a/src/vendor/geniv3rpc/ext/sfa/util/sfatime.py +++ b/src/vendor/geniv3rpc/ext/sfa/util/sfatime.py @@ -50,6 +50,9 @@ def utcparse(input): t = dateutil.parser.parse(input) if t.utcoffset() is not None: t = t.utcoffset() + t.replace(tzinfo=None) + # the above line is wrong: you need to substract the utcoffset, not add it! + # a better way is: t = t.astimezone(pytz.utc) #only works if not naive + raise RuntimeException('unfixed bug in code triggered') return t elif isinstance (input, (int,float,long)): return datetime.datetime.fromtimestamp(input)