X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Ftrust%2Fauth.py;h=2120a80dd3d585e9ff12f073f95a5c56b35c3d71;hb=82d0355774b73966cbe0c838c611808dfd2f1c7a;hp=4a2fd62b7eb17c139f53d963c0dce0d29420dadd;hpb=f39077101d7c2664b5c609a47f7b733b268e5807;p=sfa.git diff --git a/sfa/trust/auth.py b/sfa/trust/auth.py index 4a2fd62b..2120a80d 100644 --- a/sfa/trust/auth.py +++ b/sfa/trust/auth.py @@ -2,9 +2,12 @@ # SfaAPI authentication # import sys +from types import StringTypes -from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \ - BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied, CredentialNotVerifiable +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 @@ -16,6 +19,7 @@ from sfa.trust.credential import Credential from sfa.trust.trustedroots import TrustedRoots from sfa.trust.hierarchy import Hierarchy from sfa.trust.sfaticket import SfaTicket +from sfa.trust.speaksfor_util import determine_speaks_for class Auth: @@ -31,41 +35,99 @@ class Auth: 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() + 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() + + # this convenience methods extracts speaking_for_xrn + # from the passed options using 'geni_speaking_for' + def checkCredentialsSpeaksFor (self, *args, **kwds): + if 'options' not in kwds: + logger.error ("checkCredentialsSpeaksFor was not passed options=options") + return + # remove the options arg + options=kwds['options']; del kwds['options'] + # compute the speaking_for_xrn arg and pass it to checkCredentials + if options is None: speaking_for_xrn=None + else: speaking_for_xrn=options.get('geni_speaking_for',None) + kwds['speaking_for_xrn']=speaking_for_xrn + return self.checkCredentials (*args, **kwds) + + # do not use mutable as default argument + # http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments + def checkCredentials(self, creds, operation, xrns=None, + check_sliver_callback=None, + speaking_for_xrn=None): + if xrns is None: xrns=[] + def log_invalid_cred(cred): + if not isinstance (cred, StringTypes): + logger.info("cannot validate credential %s - expecting a string"%cred) + error="checkCredentials: expected a string, received %s"%(type(cred)) + else: + cred_obj=Credential(string=cred) + logger.info("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") - def checkCredentials(self, creds, operation, xrns=[]): if not isinstance(xrns, list): xrns = [xrns] - hrns = [Xrn(xrn).hrn for xrn in 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 = [] 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 InsufficientRights("Access denied - no credential provided") + if not creds: raise Forbidden("no credential provided") if not hrns: hrns = [None] - for cred in creds: - for hrn in hrns: - try: - self.check(cred, operation, hrn) - valid.append(cred) - except: - cred_obj=Credential(cred=cred) - logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True)) - error = sys.exc_info()[:2] - continue - + error=[None,None] + + speaks_for_gid = determine_speaks_for(logger, creds, self.peer_cert, + speaking_for_xrn, self.trusted_cert_list) + + if self.peer_cert and \ + not self.peer_cert.is_pubkey(speaks_for_gid.get_pubkey()): + valid = creds + else: + for cred in creds: + for hrn in hrns: + try: + self.check(cred, operation, hrn) + valid.append(cred) + except: + error = log_invalid_cred(cred) + + # 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 InsufficientRights('Access denied: %s -- %s' % (error[0],error[1])) + raise Forbidden("Invalid credential %s -- %s"%(error[0],error[1])) return valid def check(self, credential, operation, hrn = None): """ - Check the credential against the peer cert (callerGID included + 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 @@ -95,7 +157,8 @@ class Auth: raise InsufficientRights(operation) if self.trusted_cert_list: - self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA) + self.client_cred.verify(self.trusted_cert_file_list, + self.config.SFA_CREDENTIAL_SCHEMA) else: raise MissingTrustedRoots(self.config.get_trustedroots_dir()) @@ -111,7 +174,7 @@ class Auth: def check_ticket(self, ticket): """ - Check if the tickt was signed by a trusted cert + Check if the ticket was signed by a trusted cert """ if self.trusted_cert_list: client_ticket = SfaTicket(string=ticket) @@ -258,7 +321,8 @@ class Auth: rl = Rights() type = reg_record.type - logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn)) + 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