def update_user(admin, user_id): """ Update the user with the given id. :param admin: Is the administrator user, determined by @adminRequired. :param user_id: Is the user id. :return: A message that the update was successful and a list of all updated fields. """ # Get the update data data = json_body() # Query the user. If he/she is not verified yet, there *must* be a # rank_id given in the update data. user = User.query.filter(User.id == user_id).first() if not user: raise exc.EntryNotFound() if not user.is_verified and 'rank_id' not in data: raise exc.UserIsNotVerified() # The password pre-check must be done here... if 'password' in data: # The repeat password must be there, too! if 'password_repeat' not in data: raise exc.DataIsMissing() # Both must be strings if not all([isinstance(x, str) for x in [data['password'], data['password_repeat']]]): raise exc.WrongType() # Passwords must match if data['password'] != data['password_repeat']: raise exc.PasswordsDoNotMatch() # Minimum password length if len(data['password']) < app.config['MINIMUM_PASSWORD_LENGTH']: raise exc.PasswordTooShort() # Convert the password into a salted hash # DONT YOU DARE TO REMOVE THIS LINE data['password'] = bcrypt.generate_password_hash(data['password']) # DONT YOU DARE TO REMOVE THIS LINE # All fine, delete repeat_password from the dict and do the rest of the update del data['password_repeat'] return generic_update(User, user_id, data, admin)
def parse_timestamp(data: dict, required: bool) -> dict: """ Parses a timestamp in a input dictionary. If there is no timestamp and it's not required, nothing happens. Otherwise an exception gets raised. If a timestamp exists, it gets parsed. :param data: The input dictionary :param required: Flag whether the timestamp is required. :return: The parsed input dictionary. """ # If the timestamp is missing but it is required, raise an exception. # Otherwise return the (non-modified) input data. if 'timestamp' not in data: if required: raise exc.DataIsMissing() else: return data # Get the timestamp timestamp = data.get('timestamp') # If the timestamp is not a string, raise an exception if not isinstance(timestamp, str): raise exc.WrongType() # Catch empty string timestamp which is caused by some JS date pickers # inputs when they get cleared. If the timestamp is required, raise an exception. if timestamp == '': if required: raise exc.DataIsMissing() else: del data['timestamp'] return data else: try: timestamp = dateutil.parser.parse(data['timestamp']) assert isinstance(timestamp, datetime.datetime) assert timestamp < datetime.datetime.now(datetime.timezone.utc) data['timestamp'] = timestamp.replace(microsecond=0) except (TypeError, ValueError, AssertionError): raise exc.InvalidData() return data
def get_product_pricehistory(admin, product_id): """ Returns the pricehistory of the product with the given id. If only want to query a part of the history in a range there are optional request arguments: - start_date: Is the unix timestamp of the start date. - end_date: Is the unix timestamp of the end date. :param admin: Is the administrator user, determined by @adminRequired. :param product_id: Is the product id. :raises EntryNotFound: If the product does not exist. :raises WrongType: If the request args are invalid. :return: The pricehistory of the product. """ # Check, whether the product exists. product = Product.query.filter(Product.id == product_id).first() if not product: raise exc.EntryNotFound() # Get the (optional) time range parameters try: start = request.args.get('start_date') if start: start = int(start) end = request.args.get('end_date') if end: end = int(end) except (TypeError, ValueError): raise exc.WrongType() # Check whether start lies before end date if start and end: if not start <= end: raise exc.InvalidData() history = product.get_pricehistory(start, end) return jsonify(history), 200
def check_allowed_parameters(allowed): """ This method checks all GET parameters for their type. :param allowed: A dictionary containing all allowed parameters and types. :return: A dictionary with all converted and checked parameters. :raises UnauthorizedAccess: If there's an illegal parameter in the data. :raises WrongType: If an argument is of the wrong type. """ result = {} if any([argument not in allowed for argument in request.args]): raise exc.UnauthorizedAccess() for key in request.args: try: result[key] = allowed[key](request.args.get(key)) except ValueError: raise exc.WrongType() return result
def check_fields_and_types(data, required, optional=None): """ This function checks the given data for its types and existence. Required fields must exist, optional fields must not. :param data: The data sent to the API. :param required: A dictionary with all required entries and their types. :param optional: A dictionary with all optional entries and their types. :return: None :raises DataIsMissing: If a required field is not in the data. :raises WrongType: If a field is of the wrong type. """ if required and optional: allowed = dict(**required, **optional) elif required: allowed = required else: allowed = optional # Check if there is an unknown field in the data if not all(x in allowed for x in data): raise exc.UnknownField() # Check whether all required data is available if required and any(item not in data for item in required): raise exc.DataIsMissing() # Check all data (including optional data) for their types for key, value in data.items(): if not isinstance(value, allowed.get(key)): raise exc.WrongType()