From bf41d25b95a9146611ad72e348f6bf0b8e9ffc2f Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Wed, 21 May 2014 22:31:36 -0400 Subject: [PATCH] fix speaks for auth --- sfa/trust/auth.py | 652 +++++++++++++++++++++------------------------- 1 file changed, 290 insertions(+), 362 deletions(-) diff --git a/sfa/trust/auth.py b/sfa/trust/auth.py index 18c3d615..c5f22f41 100644 --- a/sfa/trust/auth.py +++ b/sfa/trust/auth.py @@ -1,362 +1,290 @@ -# -# SfaAPI authentication -# -import sys - -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 - -from sfa.trust.gid import GID -from sfa.trust.rights import Rights -from sfa.trust.certificate import Keypair, Certificate -from sfa.trust.credential import Credential -from sfa.trust.trustedroots import TrustedRoots -from sfa.trust.hierarchy import Hierarchy -from sfa.trust.sfaticket import SfaTicket - - -class Auth: - """ - Credential based authentication - """ - - def __init__(self, peer_cert = None, config = None ): - self.peer_cert = peer_cert - self.hierarchy = Hierarchy() - if not config: - self.config = Config() - 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() - - def checkCredentials(self, creds, operation, xrns=[], check_sliver_callback=None, speaking_for_hrn=None): - - def log_invalid_cred(cred): - cred_obj=Credential(string=cred) - logger.debug("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") - - - if not isinstance(xrns, list): - xrns = [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 = [] - speaks_for_cred = None - 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 Forbidden("no credential provided") - if not hrns: hrns = [None] - error=[None,None] - for cred in creds: - for hrn in hrns: - try: - self.check(cred, operation, hrn) - valid.append(cred) - except: - if speaking_for_hrn: - try: - self.check(cred, operation, speaking_for_hrn) - speaks_for_cred = cred - valid.append(cred) - except: - error = log_invalid_cred(cred) - else: - error = log_invalid_cred(cred) - continue - - # 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 Forbidden("Invalid credential %s -- %s"%(error[0],error[1])) - - if speaking_for_hrn and not speaks_for_cred: - raise InsufficientRights('Access denied: "geni_speaking_for" option specified but no valid speaks for credential found: %s -- %s' % (error[0],error[1])) - - return valid - - - def check(self, credential, operation, hrn = None): - """ - 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 - the specified operation. - """ - cred = Credential(cred=credential) - self.client_cred = cred - logger.debug("Auth.check: handling hrn=%s and credential=%s"%\ - (hrn,cred.get_summary_tostring())) - - if cred.type not in ['geni_sfa']: - raise CredentialNotVerifiable(cred.type, "%s not supported" % cred.type) - self.client_gid = self.client_cred.get_gid_caller() - self.object_gid = self.client_cred.get_gid_object() - - # make sure the client_gid is not blank - if not self.client_gid: - raise MissingCallerGID(self.client_cred.get_subject()) - - # validate the client cert if it exists - if self.peer_cert: - self.verifyPeerCert(self.peer_cert, self.client_gid) - - # make sure the client is allowed to perform the operation - if operation: - if not self.client_cred.can_perform(operation): - raise InsufficientRights(operation) - - if self.trusted_cert_list: - self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA) - else: - raise MissingTrustedRoots(self.config.get_trustedroots_dir()) - - # Make sure the credential's target matches the specified hrn. - # This check does not apply to trusted peers - trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list] - if hrn and self.client_gid.get_hrn() not in trusted_peers: - target_hrn = self.object_gid.get_hrn() - if not hrn == target_hrn: - raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \ - (target_hrn, hrn) ) - return True - - def check_ticket(self, ticket): - """ - Check if the tickt was signed by a trusted cert - """ - if self.trusted_cert_list: - client_ticket = SfaTicket(string=ticket) - client_ticket.verify_chain(self.trusted_cert_list) - else: - raise MissingTrustedRoots(self.config.get_trustedroots_dir()) - - return True - - def verifyPeerCert(self, cert, gid): - # make sure the client_gid matches client's certificate - if not cert.is_pubkey(gid.get_pubkey()): - raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject()) - - def verifyGidRequestHash(self, gid, hash, arglist): - key = gid.get_pubkey() - if not key.verify_string(str(arglist), hash): - raise BadRequestHash(hash) - - def verifyCredRequestHash(self, cred, hash, arglist): - gid = cred.get_gid_caller() - self.verifyGidRequestHash(gid, hash, arglist) - - def validateGid(self, gid): - if self.trusted_cert_list: - gid.verify_chain(self.trusted_cert_list) - - def validateCred(self, cred): - if self.trusted_cert_list: - cred.verify(self.trusted_cert_file_list) - - def authenticateGid(self, gidStr, argList, requestHash=None): - gid = GID(string = gidStr) - self.validateGid(gid) - # request_hash is optional - if requestHash: - self.verifyGidRequestHash(gid, requestHash, argList) - return gid - - def authenticateCred(self, credStr, argList, requestHash=None): - cred = Credential(string = credStr) - self.validateCred(cred) - # request hash is optional - if requestHash: - self.verifyCredRequestHash(cred, requestHash, argList) - return cred - - def authenticateCert(self, certStr, requestHash): - cert = Certificate(string=certStr) - # xxx should be validateCred ?? - self.validateCred(cert) - - def gidNoop(self, gidStr, value, requestHash): - self.authenticateGid(gidStr, [gidStr, value], requestHash) - return value - - def credNoop(self, credStr, value, requestHash): - self.authenticateCred(credStr, [credStr, value], requestHash) - return value - - def verify_cred_is_me(self, credential): - is_me = False - cred = Credential(string=credential) - caller_gid = cred.get_gid_caller() - caller_hrn = caller_gid.get_hrn() - if caller_hrn != self.config.SFA_INTERFACE_HRN: - raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN) - - return - - def get_auth_info(self, auth_hrn): - """ - Given an authority name, return the information for that authority. - This is basically a stub that calls the hierarchy module. - - @param auth_hrn human readable name of authority - """ - - return self.hierarchy.get_auth_info(auth_hrn) - - - def veriry_auth_belongs_to_me(self, name): - """ - Verify that an authority belongs to our hierarchy. - This is basically left up to the implementation of the hierarchy - module. If the specified name does not belong, ane exception is - thrown indicating the caller should contact someone else. - - @param auth_name human readable name of authority - """ - - # get auth info will throw an exception if the authority doesnt exist - self.get_auth_info(name) - - - def verify_object_belongs_to_me(self, name): - """ - Verify that an object belongs to our hierarchy. By extension, - this implies that the authority that owns the object belongs - to our hierarchy. If it does not an exception is thrown. - - @param name human readable name of object - """ - auth_name = self.get_authority(name) - if not auth_name: - auth_name = name - if name == self.config.SFA_INTERFACE_HRN: - return - self.verify_auth_belongs_to_me(auth_name) - - def verify_auth_belongs_to_me(self, name): - # get auth info will throw an exception if the authority doesnt exist - self.get_auth_info(name) - - - def verify_object_permission(self, name): - """ - Verify that the object gid that was specified in the credential - allows permission to the object 'name'. This is done by a simple - prefix test. For example, an object_gid for plc.arizona would - match the objects plc.arizona.slice1 and plc.arizona. - - @param name human readable name to test - """ - object_hrn = self.object_gid.get_hrn() - if object_hrn == name: - return - if name.startswith(object_hrn + "."): - return - #if name.startswith(get_authority(name)): - #return - - raise PermissionError(name) - - def determine_user_rights(self, caller_hrn, reg_record): - """ - Given a user credential and a record, determine what set of rights the - user should have to that record. - - This is intended to replace determine_user_rights() and - verify_cancreate_credential() - """ - - rl = Rights() - type = reg_record.type - - 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 - researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ] - # locating PIs attached to that slice - slice_pis=reg_record.get_pis() - pi_hrns = [ user.hrn for user in slice_pis ] - if (caller_hrn in researcher_hrns + pi_hrns): - rl.add('refresh') - rl.add('embed') - rl.add('bind') - rl.add('control') - rl.add('info') - - elif type == 'authority': - pi_hrns = [ user.hrn for user in reg_record.reg_pis ] - if (caller_hrn == self.config.SFA_INTERFACE_HRN): - rl.add('authority') - rl.add('sa') - rl.add('ma') - if (caller_hrn in pi_hrns): - rl.add('authority') - rl.add('sa') - # NOTE: for the PL implementation, this 'operators' list - # amounted to users with 'tech' role in that site - # it seems like this is not needed any longer, so for now I just drop that - # operator_hrns = reg_record.get('operator',[]) - # if (caller_hrn in operator_hrns): - # rl.add('authority') - # rl.add('ma') - - elif type == 'user': - rl.add('refresh') - rl.add('resolve') - rl.add('info') - - elif type == 'node': - rl.add('operator') - - return rl - - def get_authority(self, hrn): - return get_authority(hrn) - - def filter_creds_by_caller(self, creds, caller_hrn_list): - """ - Returns a list of creds who's gid caller matches the - specified caller hrn - """ - if not isinstance(creds, list): - creds = [creds] - creds = [] - if not isinstance(caller_hrn_list, list): - caller_hrn_list = [caller_hrn_list] - for cred in creds: - try: - tmp_cred = Credential(string=cred) - if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: - creds.append(cred) - except: pass - return creds - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Can the ticket be delegated? + + + + + + A desciption of the resources that are being promised + + + + + + + + + The ticket must be "cashed in" by this date + + + + + + + + + + + + + + + + An ABAC RT0 statement, used only for type 'abac'. + + + + + + + + + + + + + + + + + + + + + + + + + + + An ABAC assertion containing a single RT0 statement, used only for type 'abac'. + + + + + + + + + + + + + + + + + + A credential granting privileges or a ticket or making an ABAC assertion. + + + + + + + + + + + + + + + + + + + Privileges or a ticket or an ABAC assertion + + + + + + + + + + + + + + + The type of this credential. Currently a Privilege set or a Ticket or ABAC. + + + + + + + + + + + + + A serial number. + + + + + GID of the owner of this credential. + + + + + URN of the owner. Not everyone can parse DER + + + + + GID of the target of this credential. + + + + + URN of the target. + + + + + UUID of this credential + + + + + Expires on in ISO8601 format but preferably RFC3339 + + + + + Optional Extensions + + + + + + + + + Parent that delegated to us + + + + + + + + + + + + + + -- 2.43.0