def validate_user_cert(user_cert, signing_user_id, check_admin=False): """ Given a deserialized user certificate, check its validity: * make sure user identified by signing_user_id exists * make sure the admin identified in user_cert exists, if check_admin is set * verify that the user identified by signing_user_id has signed the cert Return the signing user and the admin user on success Raise an exception on error """ from common.api import verify_data signing_user_fut = SyndicateUser.Read(signing_user_id, async=True) admin_user_fut = None if user_cert.admin_id != signing_user_id: admin_user_fut = SyndicateUser.Read(user_cert.admin_id, async=True) storagetypes.wait_futures([signing_user_fut, admin_user_fut]) else: storagetypes.wait_futures([signing_user_fut]) signing_user = signing_user_fut.get_result() if admin_user_fut is not None: admin_user = admin_user_fut.get_result() else: # signing user and admin are the same admin_user = signing_user if check_admin: if admin_user is None: raise Exception( "The admin (%s) who signed this user cert does not exist" % user_cert.admin_id) if not admin_user.is_admin: raise Exception("User '%s' is not an admin" % (admin_user.email)) if signing_user is None: raise Exception("No such user '%s'" % signing_user_id) user_pubkey = signing_user.public_key user_sig = user_cert.signature user_cert.signature = "" user_cert_nosigs = user_cert.SerializeToString() rc = verify_data(user_pubkey, user_cert_nosigs, base64.b64decode(user_sig)) user_cert.signature = user_sig if not rc: raise Exception("User '%s' did not sign cert" % (signing_user.email)) return signing_user, admin_user
def validate_user_cert( user_cert, signing_user_id, check_admin=False ): """ Given a deserialized user certificate, check its validity: * make sure user identified by signing_user_id exists * make sure the admin identified in user_cert exists, if check_admin is set * verify that the user identified by signing_user_id has signed the cert Return the signing user and the admin user on success Raise an exception on error """ from common.api import verify_data signing_user_fut = SyndicateUser.Read( signing_user_id, async=True ) admin_user_fut = None if user_cert.admin_id != signing_user_id: admin_user_fut = SyndicateUser.Read( user_cert.admin_id, async=True ) storagetypes.wait_futures( [signing_user_fut, admin_user_fut] ) else: storagetypes.wait_futures( [signing_user_fut] ) signing_user = signing_user_fut.get_result() if admin_user_fut is not None: admin_user = admin_user_fut.get_result() else: # signing user and admin are the same admin_user = signing_user if check_admin: if admin_user is None: raise Exception("The admin (%s) who signed this user cert does not exist" % user_cert.admin_id ) if not admin_user.is_admin: raise Exception("User '%s' is not an admin" % (admin_user.email)) if signing_user is None: raise Exception("No such user '%s'" % signing_user_id ) user_pubkey = signing_user.public_key user_sig = user_cert.signature user_cert.signature = "" user_cert_nosigs = user_cert.SerializeToString() rc = verify_data( user_pubkey, user_cert_nosigs, base64.b64decode( user_sig ) ) user_cert.signature = user_sig if not rc: raise Exception("User '%s' did not sign cert" % (signing_user.email)) return signing_user, admin_user
def _read_user_and_volume( email_or_id, volume_name_or_id ): user_fut = SyndicateUser.Read( email_or_id, async=True ) volume_fut = Volume.Read( volume_name_or_id, async=True ) storagetypes.wait_futures( [user_fut, volume_fut] ) user = user_fut.get_result() volume = volume_fut.get_result() return user, volume
def _read_user_and_volume(email_or_id, volume_name_or_id): user_fut = SyndicateUser.Read(email_or_id, async=True) volume_fut = Volume.Read(volume_name_or_id, async=True) storagetypes.wait_futures([user_fut, volume_fut]) user = user_fut.get_result() volume = volume_fut.get_result() return user, volume
def list_volume_user_ids( volume_name_or_id, **q_opts ): caller_user = _check_authenticated( q_opts ) volume_id = _get_volume_id( volume_name_or_id ) def __user_from_access_request( req ): return SyndicateUser.Read_ByOwnerID( req.requester_owner_id, async=True ) q_opts["map_func"] = __user_from_access_request user_futs = VolumeAccessRequest.ListAll( {"VolumeAccessRequest.volume_id ==": volume_id}, **q_opts ) storagetypes.wait_futures( user_futs ) ret = [u.email for u in filter( lambda x: x != None, [uf.get_result() for uf in user_futs] )] return ret
def _read_user_and_volume(email, volume_name_or_id): user_fut = SyndicateUser.Read(email, async=True) volume_fut = Volume.Read(volume_name_or_id, async=True) storagetypes.wait_futures([user_fut, volume_fut]) user = user_fut.get_result() volume = volume_fut.get_result() if user == None: raise Exception("No such User '%s'" % email) if volume == None: raise Exception("No such Volume '%s'" % volume_name_or_id) return user, volume
def _read_user_and_volume( email, volume_name_or_id ): user_fut = SyndicateUser.Read( email, async=True ) volume_fut = Volume.Read( volume_name_or_id, async=True ) storagetypes.wait_futures( [user_fut, volume_fut] ) user = user_fut.get_result() volume = volume_fut.get_result() if user == None: raise Exception("No such User '%s'" % email) if volume == None: raise Exception("No such Volume '%s'" % volume_name_or_id ) return user, volume
def list_volume_user_ids(volume_name_or_id, **q_opts): caller_user = _check_authenticated(q_opts) volume_id = _get_volume_id(volume_name_or_id) def __user_from_access_request(req): return SyndicateUser.Read_ByOwnerID(req.requester_owner_id, async=True) q_opts["map_func"] = __user_from_access_request user_futs = VolumeAccessRequest.ListAll( {"VolumeAccessRequest.volume_id ==": volume_id}, **q_opts) storagetypes.wait_futures(user_futs) ret = [ u.email for u in filter(lambda x: x != None, [uf.get_result() for uf in user_futs]) ] return ret
def validate_cert_bundle( cert_bundle, user, volume_id, volume_version, new_gateway_cert=None ): """ Given a deserialized cert bundle, check its validity: * make sure the user it points to exists * make sure it has the right volume ID (load from the datastore by default, or use volume_id or volume kw if given) * make sure the user signed it, or verify that the user identified by signing_user_id did. * make sure its volume version is equal to or exceeds the given volume_version * make sure the cert bundle's timestamp exceeds the previous cert bundle's timestamp * make sure each of its gateway versions are equal to or exceeds the local copies of the gateway certs' versions --- if new_gateway_cert is given, it will be checked in place of an on-file gateway cert. This is useful when updating a gateway--we need to check the gateway cert that will be written, not the one on file. Return True if so Raise an exception on error. """ from common.api import verify_data if user.owner_id != cert_bundle.owner_id: raise Exception("Invalid cert bundle: invalid owner ID") # verify the user signed the cert owner_pubkey = user.public_key owner_sig = cert_bundle.signature cert_bundle.signature = "" cert_bundle_nosigs = cert_bundle.SerializeToString() rc = verify_data( owner_pubkey, cert_bundle_nosigs, base64.b64decode( owner_sig ) ) cert_bundle.signature = owner_sig if not rc: raise Exception("Cert bundle not signed by user '%s'" % signing_user_id) # volume ID must match if volume_id != cert_bundle.volume_id: raise Exception("Invalid volume ID: %s != %s" % (volume_id, cert_bundle.volume_id)) # volume version must increase or stay the same # NOTE: Manifest.file_version encodes the volume version, when being used as a cert bundle if volume_version > cert_bundle.file_version: raise Exception("Stale volume version for %s: expected > %s, got %s" % (volume_id, volume_version, cert_bundle.file_version)) # get the previous cert bundle prev_cert_bundle_rec = VolumeCertBundle.Get( volume_id ) if prev_cert_bundle_rec is not None: # this cert bundle's timestamp must be newer prev_cert_bundle = VolumeCertBundle.Load( prev_cert_bundle_rec ) if prev_cert_bundle is not None: if prev_cert_bundle.mtime_sec > cert_bundle.mtime_sec or (prev_cert_bundle.mtime_sec == cert_bundle.mtime_sec and prev_cert_bundle.mtime_nsec > cert_bundle.mtime_nsec): # stale raise Exception("Stale cert bundle for %s: expected > %s.%s, got %s.%s" % (volume_id, prev_cert_bundle.mtime_sec, prev_cert_bundle.mtime_nsec, cert_bundle.mtime_sec, cert_bundle.mtime_nsec)) gateway_cert_and_fut = {} # map gateway ID to (gateway cert block, gateway future) all_futs = [] # cert is valid. # validate volume cert--it must match information in the cert manifest if len(cert_bundle.blocks) == 0: raise Exception("Missing volume cert in cert bundle") volume_cert_block = cert_bundle.blocks[0] if volume_cert_block.block_id != volume_id: raise Exception("Cert block for volume does not match volume ID (expected %s, got %s)" % (volume_id, volume_cert_block.block_id)) if volume_cert_block.block_version != cert_bundle.file_version: raise Exception("Cert block for volume does not match volume version (expected %s, got %s)" % (cert_bundle.file_version, volume_cert_block.block_version)) if volume_cert_block.owner_id != user.owner_id: raise Exception("Cert block for volume does not match owner ID (expected %s, got %s)" % (user.owner_id, volume_cert_block.owner_id)) # go validate the affected gateways # NOTE: blocks[0] contains the volume cert (used by gateways) for i in xrange( 1, len(cert_bundle.blocks) ): block = cert_bundle.blocks[i] gateway_id = block.block_id if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # skip this one--we'll check below continue gateway_fut = Gateway.Read( gateway_id, async=True ) gateway_cert_and_fut[ gateway_id ] = (block, gateway_fut) all_futs.append( gateway_fut ) storagetypes.wait_futures( all_futs ) for (gateway_id, (block, gateway_fut)) in gateway_cert_and_fut.items(): # from the cert bundle given_gateway_version = None given_gateway_caps = None # on file gateway_version = None gateway_caps = None if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # make sure the new gateway cert has an appropriate version given_gateway_version = new_gateway_cert.gateway_version given_gateway_caps = new_gateway_cert.caps # don't worry about the caps--we'll be updating them shortly gateway_version = gateway.version gateway_caps = new_gateway_cert.caps else: # use the gateway on file gateway = gateway_fut.get_result() if gateway is None: # requested a gateway that didn't exist raise Exception("Cert bundle identifies non-existant gateway") if gateway.g_id != gateway_id: # this is a bug--the gateway is keyed by id raise Exception("BUG: requested %s, got %s" % (gateway_id, gateway.g_id)) given_gateway_caps = block.caps given_gateway_version = gateway.cert_version # can only check new gateway certs # check against on-file cert gateway_version = gateway.cert_version gateway_caps = gateway.caps # gateway version must increase or stay the same if gateway_version > given_gateway_version: # stale version raise Exception("Stale version for %s: expected >= %s, got %s" % (gateway_id, gateway_version, given_gateway_version)) if (gateway_caps | given_gateway_caps) != given_gateway_caps: # invalid caps (only works when checked against on-file gateways) raise Exception("Invalid caps for %s: expected %s, got %s" % (gateway_id, gateway.caps, given_gateway_caps)) # valid! return True
def validate_cert_bundle(cert_bundle, user, volume_id, volume_version, new_gateway_cert=None): """ Given a deserialized cert bundle, check its validity: * make sure the user it points to exists * make sure it has the right volume ID (load from the datastore by default, or use volume_id or volume kw if given) * make sure the user signed it, or verify that the user identified by signing_user_id did. * make sure its volume version is equal to or exceeds the given volume_version * make sure the cert bundle's timestamp exceeds the previous cert bundle's timestamp * make sure each of its gateway versions are equal to or exceeds the local copies of the gateway certs' versions --- if new_gateway_cert is given, it will be checked in place of an on-file gateway cert. This is useful when updating a gateway--we need to check the gateway cert that will be written, not the one on file. Return True if so Raise an exception on error. """ from common.api import verify_data if user.owner_id != cert_bundle.owner_id: raise Exception("Invalid cert bundle: invalid owner ID") # verify the user signed the cert owner_pubkey = user.public_key owner_sig = cert_bundle.signature cert_bundle.signature = "" cert_bundle_nosigs = cert_bundle.SerializeToString() rc = verify_data(owner_pubkey, cert_bundle_nosigs, base64.b64decode(owner_sig)) cert_bundle.signature = owner_sig if not rc: raise Exception("Cert bundle not signed by user '%s'" % signing_user_id) # volume ID must match if volume_id != cert_bundle.volume_id: raise Exception("Invalid volume ID: %s != %s" % (volume_id, cert_bundle.volume_id)) # volume version must increase or stay the same # NOTE: Manifest.file_version encodes the volume version, when being used as a cert bundle if volume_version > cert_bundle.file_version: raise Exception("Stale volume version for %s: expected > %s, got %s" % (volume_id, volume_version, cert_bundle.file_version)) # get the previous cert bundle prev_cert_bundle_rec = VolumeCertBundle.Get(volume_id) if prev_cert_bundle_rec is not None: # this cert bundle's timestamp must be newer prev_cert_bundle = VolumeCertBundle.Load(prev_cert_bundle_rec) if prev_cert_bundle is not None: if prev_cert_bundle.mtime_sec > cert_bundle.mtime_sec or ( prev_cert_bundle.mtime_sec == cert_bundle.mtime_sec and prev_cert_bundle.mtime_nsec > cert_bundle.mtime_nsec): # stale raise Exception( "Stale cert bundle for %s: expected > %s.%s, got %s.%s" % (volume_id, prev_cert_bundle.mtime_sec, prev_cert_bundle.mtime_nsec, cert_bundle.mtime_sec, cert_bundle.mtime_nsec)) gateway_cert_and_fut = { } # map gateway ID to (gateway cert block, gateway future) all_futs = [] # cert is valid. # validate volume cert--it must match information in the cert manifest if len(cert_bundle.blocks) == 0: raise Exception("Missing volume cert in cert bundle") volume_cert_block = cert_bundle.blocks[0] if volume_cert_block.block_id != volume_id: raise Exception( "Cert block for volume does not match volume ID (expected %s, got %s)" % (volume_id, volume_cert_block.block_id)) if volume_cert_block.block_version != cert_bundle.file_version: raise Exception( "Cert block for volume does not match volume version (expected %s, got %s)" % (cert_bundle.file_version, volume_cert_block.block_version)) if volume_cert_block.owner_id != user.owner_id: raise Exception( "Cert block for volume does not match owner ID (expected %s, got %s)" % (user.owner_id, volume_cert_block.owner_id)) # go validate the affected gateways # NOTE: blocks[0] contains the volume cert (used by gateways) for i in xrange(1, len(cert_bundle.blocks)): block = cert_bundle.blocks[i] gateway_id = block.block_id if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # skip this one--we'll check below continue gateway_fut = Gateway.Read(gateway_id, async=True) gateway_cert_and_fut[gateway_id] = (block, gateway_fut) all_futs.append(gateway_fut) storagetypes.wait_futures(all_futs) for (gateway_id, (block, gateway_fut)) in gateway_cert_and_fut.items(): # from the cert bundle given_gateway_version = None given_gateway_caps = None # on file gateway_version = None gateway_caps = None if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # make sure the new gateway cert has an appropriate version given_gateway_version = new_gateway_cert.gateway_version given_gateway_caps = new_gateway_cert.caps # don't worry about the caps--we'll be updating them shortly gateway_version = gateway.version gateway_caps = new_gateway_cert.caps else: # use the gateway on file gateway = gateway_fut.get_result() if gateway is None: # requested a gateway that didn't exist raise Exception( "Cert bundle identifies non-existant gateway %s" % gateway_id) if gateway.g_id != gateway_id: # this is a bug--the gateway is keyed by id raise Exception("BUG: requested %s, got %s" % (gateway_id, gateway.g_id)) given_gateway_caps = block.caps given_gateway_version = gateway.cert_version # can only check new gateway certs # check against on-file cert gateway_version = gateway.cert_version gateway_caps = gateway.caps # gateway version must increase or stay the same if gateway_version > given_gateway_version: # stale version raise Exception( "Stale version for %s: expected >= %s, got %s" % (gateway_id, gateway_version, given_gateway_version)) if (gateway_caps | given_gateway_caps) != given_gateway_caps: # invalid caps (only works when checked against on-file gateways) raise Exception("Invalid caps for %s: expected %s, got %s" % (gateway_id, gateway.caps, given_gateway_caps)) # valid! return True