def regenerate_api_key(geniuser): """ <Purpose> Regenerates the user's API key. <Arguments> geniuser A GeniUser object of the user whose api key is to be regenerated. <Exceptions> None <Side Effects> The API key for the user is updated in the database. <Returns> The new API key. """ assert_geniuser(geniuser) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect changes we make to the object. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) return maindb.regenerate_api_key(geniuser) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def ban_user_and_remove_vessels(username): try: geniuser = maindb.get_user(username, allow_inactive=True) except DoesNotExistError: print "No such user: %s." % username sys.exit(1) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: if geniuser.is_active: geniuser.is_active = False geniuser.save() print "This account has been set to inactive (banned)." else: print "This account is already inactive (banned)." acquired_vessels = maindb.get_acquired_vessels(geniuser) if not acquired_vessels: print "No acquired vessels to stop/remove access to." else: print "Vessels acquired by this user: %s" % acquired_vessels print "Indicating to the backend to release %s vessels." % len( acquired_vessels) vessels.release_vessels(lockserver_handle, geniuser, acquired_vessels) print "Release indicated. Monitoring db to see if the backend cleaned them up." while True: for vessel in acquired_vessels[:]: updated_vessel = maindb.get_vessel( vessel.node.node_identifier, vessel.name) if vessel.node.is_broken or not vessel.node.is_active: print "Node %s is broken or inactive, so backend won't contact it." % vessel.node acquired_vessels.remove(vessel) continue if updated_vessel.is_dirty: print "Vessel %s has not been cleaned up yet." % updated_vessel else: print "Vessel %s has been cleaned up." % updated_vessel acquired_vessels.remove(vessel) if not acquired_vessels: print "All vessels have been cleaned up." break else: print "%s vessels remain to be cleaned up." % len( acquired_vessels) print "Sleeping 10 seconds." time.sleep(10) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def change_user_keys(geniuser, pubkey=None): """ <Purpose> Sets a new public/private key for the user and initiates the change of user keys on all vessels the user has access to. If pubkey is provided, that is used as the user's new pubkey. If pubkey is not provided, a new public/private keypair is generated for the user. <Arguments> geniuser A GeniUser object of the user whose keys are to be updated. <Exceptions> ValidationError If the pubkey is provided and is invalid. <Side Effects> The public and private keys of the user are replaced in the database with new keys (if pubkey was provided, the private key in the database will be empty, otherwise it will be the generated private key). All vessels the user has access to are marked as needing to have their user keys sync'd. <Returns> None """ assert_geniuser(geniuser) if pubkey is not None: validations.validate_pubkey_string(pubkey) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect changes we make to the object. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Get a key pair from the keygen api if the user didn't supply their own pubkey. if pubkey is None: (pubkey, privkey) = keygen.generate_keypair() else: privkey = None maindb.set_user_keys(geniuser, pubkey, privkey) # Now we need to find all of the vessels the user has access to and set # them to have their user keys updated by the backend. vessel_list = maindb.get_vessels_accessible_by_user(geniuser) if vessel_list: vessels.flag_vessels_for_user_keys_sync(lockserver_handle, vessel_list) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def renew_vessels(geniuser, vessel_list): """ <Purpose> Extend the expiration dates of vessels acquired by a user. <Arguments> geniuser The GeniUser whose vessels are to be renewed. vessel_list A list of vessels to be renewed. <Exceptions> InvalidRequestError If any of the vessels in the vessel_list are not currently acquired by geniuser or if the list of vessels is empty. InsufficientUserResourcesError If the user is currently over their limit of acquired resources. <Side Effects> The vessels are renewed to the maximum time vessels can be acquired for, regardless of their previous individual expiration times. <Returns> None """ assert_geniuser(geniuser) assert_list(vessel_list) for vessel in vessel_list: assert_vessel(vessel) if not vessel_list: raise InvalidRequestError("The list of vessels cannot be empty.") # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Ensure the user is not over their limit of acquired vessels due to # donations of theirs having gone offline. This call will raise an # InsufficientUserResourcesError if the user is currently over their # limit. vesselcount = 0 maindb.require_user_can_acquire_resources(geniuser, vesselcount) # The vessels.renew_vessels function is responsible for ensuring that the # vessels belong to this user. We let the other function do the check # because we want to hold locks on the vessels' nodes before checking. vessels.renew_vessels(lockserver_handle, geniuser, vessel_list) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def ban_user_and_remove_vessels(username): try: geniuser = maindb.get_user(username, allow_inactive=True) except DoesNotExistError: print "No such user: %s." % username sys.exit(1) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: if geniuser.is_active: geniuser.is_active = False geniuser.save() print "This account has been set to inactive (banned)." else: print "This account is already inactive (banned)." acquired_vessels = maindb.get_acquired_vessels(geniuser) if not acquired_vessels: print "No acquired vessels to stop/remove access to." else: print "Vessels acquired by this user: %s" % acquired_vessels print "Indicating to the backend to release %s vessels." % len(acquired_vessels) vessels.release_vessels(lockserver_handle, geniuser, acquired_vessels) print "Release indicated. Monitoring db to see if the backend cleaned them up." while True: for vessel in acquired_vessels[:]: updated_vessel = maindb.get_vessel(vessel.node.node_identifier, vessel.name) if vessel.node.is_broken or not vessel.node.is_active: print "Node %s is broken or inactive, so backend won't contact it." % vessel.node acquired_vessels.remove(vessel) continue if updated_vessel.is_dirty: print "Vessel %s has not been cleaned up yet." % updated_vessel else: print "Vessel %s has been cleaned up." % updated_vessel acquired_vessels.remove(vessel) if not acquired_vessels: print "All vessels have been cleaned up." break else: print "%s vessels remain to be cleaned up." % len(acquired_vessels) print "Sleeping 10 seconds." time.sleep(10) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def acquire_specific_vessels(geniuser, vessel_list): """ <Purpose> Attempt to acquire specific vessels for a user. <Arguments> geniuser The GeniUser which will be assigned the vessels. vessel_list A list of vessels to be acquired for the user. <Exceptions> InsufficientUserResourcesError The user does not have enough vessel credits to acquire the number of vessels requested. InvalidRequestError If the list of vessels is empty. <Side Effects> Zero or more of the vessels in vessel_list have been acquired by the user. <Returns> A list of the vessels acquired as a result of this function call. The length of this list may be less than the length of vessel_list if one or more of the vessels in vessel_list could not be acquired. """ assert_geniuser(geniuser) assert_list(vessel_list) for vessel in vessel_list: assert_vessel(vessel) if not vessel_list: raise InvalidRequestError("The list of vessels cannot be empty.") # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Ensure the user is allowed to acquire these resources. This call will # raise an InsufficientUserResourcesError if the additional vessels would # cause the user to be over their limit. maindb.require_user_can_acquire_resources(geniuser, len(vessel_list)) return vessels.acquire_specific_vessels_best_effort( lockserver_handle, geniuser, vessel_list) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def acquire_specific_vessels(geniuser, vessel_list): """ <Purpose> Attempt to acquire specific vessels for a user. <Arguments> geniuser The GeniUser which will be assigned the vessels. vessel_list A list of vessels to be acquired for the user. <Exceptions> InsufficientUserResourcesError The user does not have enough vessel credits to acquire the number of vessels requested. InvalidRequestError If the list of vessels is empty. <Side Effects> Zero or more of the vessels in vessel_list have been acquired by the user. <Returns> A list of the vessels acquired as a result of this function call. The length of this list may be less than the length of vessel_list if one or more of the vessels in vessel_list could not be acquired. """ assert_geniuser(geniuser) assert_list(vessel_list) for vessel in vessel_list: assert_vessel(vessel) if not vessel_list: raise InvalidRequestError("The list of vessels cannot be empty.") # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Ensure the user is allowed to acquire these resources. This call will # raise an InsufficientUserResourcesError if the additional vessels would # cause the user to be over their limit. maindb.require_user_can_acquire_resources(geniuser, len(vessel_list)) return vessels.acquire_specific_vessels_best_effort(lockserver_handle, geniuser, vessel_list) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def release_vessels(geniuser, vessel_list): """ <Purpose> Remove a user from a vessel that is assigned to the user. <Arguments> geniuser The GeniUser who is to be removed from the vessel. vessel_list A list of vessels the user is to be removed from. <Exceptions> InvalidRequestError If any of the vessels in the vessel_list are not currently acquired by geniuser or if the list of vessels is empty. <Side Effects> The vessel is no longer assigned to the user. If this was the last user assigned to the vessel, the vessel is freed. <Returns> None """ assert_geniuser(geniuser) assert_list(vessel_list) for vessel in vessel_list: assert_vessel(vessel) if not vessel_list: raise InvalidRequestError("The list of vessels cannot be empty.") # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) vessels.release_vessels(lockserver_handle, geniuser, vessel_list) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def change_user_affiliation(geniuser, new_affiliation): """ <Purpose> Sets a new affiliation for the user <Arguments> geniuser A GeniUser object of the user whose affiliation is to be updated. new_affiliation the new affiliation value <Exceptions> ValidationError If the affiliation is provided and is invalid. <Side Effects> The geniuser affiliation gets changed to the new value(in the db). <Returns> None """ assert_geniuser(geniuser) #Determines if the new affiliation is valid. The frontend should already #checks for this but we validate again here just in case. validations.validate_affiliation(new_affiliation) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect changes we make to the object. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) maindb.set_user_affiliation(geniuser, new_affiliation) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def change_user_password(geniuser, new_password): """ <Purpose> Sets a new password for the geniuser. <Arguments> geniuser A GeniUser object of the user whose password is to be changed. new_password the user specificed new password value. <Exceptions> ValidationError If the password is provided and is invalid. <Side Effects> The geniuser password gets changed to the new value (in the db). <Returns> None """ assert_geniuser(geniuser) # Determines if the new password is strong enough. The frontend should already # check for this but we validate again here just in case. validations.validate_password(new_password) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect changes we make to the object. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) maindb.set_user_password(geniuser, new_password) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def delete_private_key(geniuser): """ <Purpose> Deletes the private key of the specified user. <Arguments> geniuser A GeniUser object of the user whose private key is to be deleted. <Exceptions> None <Side Effects> The private key belonging to the user is deleted if it exists, otherwise the user account is not modified. <Returns> None """ assert_geniuser(geniuser) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect the deletion of the key. # That is, we want the object passed in to have the user_privkey be None # when this function returns. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) maindb.delete_user_private_key(geniuser) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def change_user_port(geniuser, new_port): """ <Purpose> Sets a new port for the user <Arguments> geniuser A GeniUser object of the user whose port is to be changed. new_port the new port value <Exceptions> ValidationError If the port is provided and it is not in the allowed range. <Side Effects> the geniuser port gets changed to the new value(in the db). <Returns> None """ assert_geniuser(geniuser) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. # We don't use the user object we retrieve because we want the # object passed in to the function to reflect changes we make to the object. try: maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) maindb.set_user_port(geniuser, new_port) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def acquire_vessels(geniuser, vesselcount, vesseltype): """ <Purpose> Acquire unused vessels of a given type for a user. For information on how the specified vesseltype affects which vessels will be considered to satisfy the request, see the type-specific functions that are called by this function. <Arguments> geniuser The GeniUser which will be assigned the vessels. vesselcount The number of vessels to acquire (a positive integer). vesseltype The type of vessels to acquire. One of either 'lan', 'wan', 'nat', or 'rand'. <Exceptions> UnableToAcquireResourcesError If not able to acquire the requested vessels (in this case, no vessels will be acquired). InsufficientUserResourcesError The user does not have enough vessel credits to acquire the number of vessels requested. <Side Effects> A total of 'vesselcount' previously-unassigned vessels of the specified vesseltype have been acquired by the user. <Returns> A list of the vessels as a result of this function call. """ assert_geniuser(geniuser) assert_positive_int(vesselcount) assert_str(vesseltype) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Ensure the user is allowed to acquire these resources. This call will # raise an InsufficientUserResourcesError if the additional vessels would # cause the user to be over their limit. maindb.require_user_can_acquire_resources(geniuser, vesselcount) if vesseltype == "wan": acquired_list = vessels.acquire_wan_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == "lan": acquired_list = vessels.acquire_lan_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == "nat": acquired_list = vessels.acquire_nat_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == "rand": acquired_list = vessels.acquire_rand_vessels(lockserver_handle, geniuser, vesselcount) else: raise ProgrammerError("Vessel type '%s' is not a valid type" % vesseltype) return acquired_list finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)
def register_user(username, password, email, affiliation, pubkey=None): """ <Purpose> Creates a user record with the specified information and sets any additional information necessary for the user record to be complete. <Arguments> username password email affiliation pubkey Optional. A string. If not provided, a key pair will be generated for this user. <Exceptions> UsernameAlreadyExistsError If there is already a user with the specified username. ValidationError If any of the arguments contains invalid values or if the username is the same as the password. <Side Effects> The user record in the django db is created as well as a user record in the corresponding user profile table that stores our custom information. A port will be assigned to the user and the user's donation keys will be set. <Returns> GeniUser instance (our GeniUser model, not the django User) corresponding to the newly registered user. """ # If the frontend code that called this function wants to know which field # is invalid, it must call the validation functions itself before making the # call to register_user(). # These will raise a ValidationError if any of the fields are invalid. # These ensure that the data is of the correct type (e.g. a string) as well as # that we like the content of the variable. validations.validate_username(username) validations.validate_password(password) validations.validate_username_and_password_different(username, password) validations.validate_email(email) validations.validate_affiliation(affiliation) if pubkey is not None: validations.validate_pubkey_string(pubkey) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, username) try: # Ensure there is not already a user with this username. try: # Raises a DoesNotExistError if the user doesn't exist. maindb.get_user(username) raise UsernameAlreadyExistsError except DoesNotExistError: # This is what we wanted: the username isn't already taken. pass # Get a key pair from the keygen api if the user didn't supply their own pubkey. if pubkey is None: (pubkey, privkey) = keygen.generate_keypair() else: privkey = None # Generate a donor key for this user. This is done through the backend # as the private key must be stored in the keydb, which the website cannot # directly access. keydescription = "donor:" + username donor_pubkey = backend.generate_key(keydescription) # Create the user record. geniuser = maindb.create_user(username, password, email, affiliation, pubkey, privkey, donor_pubkey) finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, username) lockserver.destroy_lockserver_handle(lockserver_handle) return geniuser
def acquire_vessels(geniuser, vesselcount, vesseltype): """ <Purpose> Acquire unused vessels of a given type for a user. For information on how the specified vesseltype affects which vessels will be considered to satisfy the request, see the type-specific functions that are called by this function. <Arguments> geniuser The GeniUser which will be assigned the vessels. vesselcount The number of vessels to acquire (a positive integer). vesseltype The type of vessels to acquire. One of either 'lan', 'wan', 'nat', or 'rand'. <Exceptions> UnableToAcquireResourcesError If not able to acquire the requested vessels (in this case, no vessels will be acquired). InsufficientUserResourcesError The user does not have enough vessel credits to acquire the number of vessels requested. <Side Effects> A total of 'vesselcount' previously-unassigned vessels of the specified vesseltype have been acquired by the user. <Returns> A list of the vessels as a result of this function call. """ assert_geniuser(geniuser) assert_positive_int(vesselcount) assert_str(vesseltype) # Lock the user. lockserver_handle = lockserver.create_lockserver_handle() lockserver.lock_user(lockserver_handle, geniuser.username) try: # Make sure the user still exists now that we hold the lock. Also makes # sure that we see any changes made to the user before we obtained the lock. try: geniuser = maindb.get_user(geniuser.username) except DoesNotExistError: raise InternalError(traceback.format_exc()) # Ensure the user is allowed to acquire these resources. This call will # raise an InsufficientUserResourcesError if the additional vessels would # cause the user to be over their limit. maindb.require_user_can_acquire_resources(geniuser, vesselcount) if vesseltype == 'wan': acquired_list = vessels.acquire_wan_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == 'lan': acquired_list = vessels.acquire_lan_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == 'nat': acquired_list = vessels.acquire_nat_vessels(lockserver_handle, geniuser, vesselcount) elif vesseltype == 'rand': acquired_list = vessels.acquire_rand_vessels(lockserver_handle, geniuser, vesselcount) else: raise ProgrammerError("Vessel type '%s' is not a valid type" % vesseltype) return acquired_list finally: # Unlock the user. lockserver.unlock_user(lockserver_handle, geniuser.username) lockserver.destroy_lockserver_handle(lockserver_handle)