def register(): USERS_TABLE, IDENTIFIER_FIELD, PASSWORD_FIELD, ROLE_FIELD, ACTIVE_FIELD = get_login_settings( ) # Ensure that the user has sent the required fields form = request.json if request.is_json else request.form form = filter_fields_db(form, USERS_TABLE) username = form.get(IDENTIFIER_FIELD, None) password = form.get(PASSWORD_FIELD, None) if not username or not password: raise HTTPError( 400, f"Both '{IDENTIFIER_FIELD}' and '{PASSWORD_FIELD}' are required") logger.debug(f"Register request with data {form}") # Ensure that the identifier is unique login_q = get_login_query(USERS_TABLE, IDENTIFIER_FIELD, username) other_users = dal.api_safe_query(login_q) if other_users: logger.debug(f"The identifier {username} already exists") raise HTTPError( 400, f"There already exists another user with that {IDENTIFIER_FIELD}") # Create the user object, replacing the password with the hashed one user = dict(form) user[PASSWORD_FIELD] = generate_password_hash(password) # Assign a default role to the user, if specified in the settings if settings.DEFAULT_ROLE_REGISTER: user[ROLE_FIELD] = settings.DEFAULT_ROLE_REGISTER # Assign a default active status, if the activity check is on and none has # been provided if ACTIVE_FIELD and ACTIVE_FIELD not in user: user[ACTIVE_FIELD] = settings.DEFAULT_ACTIVE_STATUS # Try to insert it in the DB # Since the /register endpoint must adapt to any possible table, # we assume that the user knows what they're doing and submits the # appropriate fields. Otherwise, the DB will just complain. register_q = get_register_user_query(USERS_TABLE, user) dal.api_safe_update(register_q) # Fetch the newly created user from the DB (some fields may have been) # automatically generated, like its ID # It should now exist user = dal.api_safe_query(login_q)[0] # If we've reached here the register is successful, generate a session token # and return it with the logged user's info logger.debug("Register OK") token = create_token(user) del user[PASSWORD_FIELD] res = {"sessionToken": token, "user": user} return jsonify(res), 200
def check_session(allowed_roles): token = request.headers.get("Token", default=None) if not token: raise HTTPError(401, "Unauthorized") try: user_data = check_token(token) u_data = settings.USER_AUTH_DATA['table'] primary = get_primary_key(u_data) res = user_data[primary] # Check if the user's role is allowed to access this endpoint role_col_name = settings.USER_AUTH_DATA.get("role", None) if role_col_name: # Only check the role if we know the role column # Find the role of the user from the user data user_role = next((v for k, v in user_data.items() if k.lower() == role_col_name.lower()), None) logger.debug( f"Allowed roles are {allowed_roles} and the user role is {user_role}" ) if user_role not in allowed_roles and "*" not in allowed_roles: raise HTTPError(401, "Unauthorized") return res except TokenError as exc: raise HTTPError(401, str(exc))
def login(): USERS_TABLE, IDENTIFIER_FIELD, PASSWORD_FIELD, ROLE_FIELD, ACTIVE_FIELD = get_login_settings( ) # Ensure that the user has sent the required fields form = request.json if request.is_json else request.form form = filter_fields_db(form, USERS_TABLE) username = form.get(IDENTIFIER_FIELD, None) password = form.get(PASSWORD_FIELD, None) if not username or not password: raise HTTPError( 400, f"Both '{IDENTIFIER_FIELD}' and '{PASSWORD_FIELD}' are required") logger.debug( f"Login request from user {username} with password {password}") # Look if there is an user with such username q = get_login_query(USERS_TABLE, IDENTIFIER_FIELD, username) users = dal.api_safe_query(q) if not users: logger.debug(f"The identifier {username} was not found") raise HTTPError(400, "User not found") # The identifier field should be unique (/register also takes care of that) # so we can just extract the first one user = users[0] # Check if the user's password matches the provided one if PASSWORD_FIELD not in user: raise HTTPError(500, f"The user has no attribute '{PASSWORD_FIELD}'") password_ok = (settings.ALLOW_CLEAR_PASSWORDS and user[PASSWORD_FIELD] == password) \ or check_password_hash(user[PASSWORD_FIELD], password) if not password_ok: logger.debug(f"Incorrect password") raise HTTPError(400, "The password is not correct") # If a column has been specified for the "is active" field, and the check # is enabled in the settings, check that the user has not been deactivated if ACTIVE_FIELD is not None and settings.CHECK_USER_IS_ACTIVE: if not user[ACTIVE_FIELD]: logger.debug("The user is deactivated, login denied") raise HTTPError(401, "This user has been deactivated") # If we've reached here the login is successful, generate a session token # and return it with the logged user's info logger.debug("Login OK") token = create_token(user) del user[PASSWORD_FIELD] res = {"sessionToken": token, "user": user} return jsonify(res), 200
def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except DatabaseError as dberr: # Grab the error message from the exception m = regex_error_str.match(str(dberr)) msg = m.group(1) if m else str(dberr) code = 400 raise HTTPError(code, msg)
def handle_generic_error(exc): # Pass through our own HTTP error exception if isinstance(exc, HTTPError): return exc # Create a similar JSON response for Werkzeug's exceptions if isinstance(exc, HTTPException): code = exc.code res = jsonify({"message": exc.description, "code": code}) return res, code # We're facing an uncontrolled server exception logger.exception(exc) exc_type = type(exc).__name__ msg = str(exc) err = HTTPError(500, msg, exc_type) return handle_HTTPError(err)
def add(email, password, departmentId, bossId, firstName, lastName, salary): if not firstName or not lastName: raise HTTPError(400, "The first and last name are required.")
def route_handler(*args, **kwargs): # If this endpoint requires authentication, check that the # user has provided a session token and that it is valid if auth_required: userId = check_session(allowed_roles) # Collect all url pattern params request_url_params_dict = kwargs # If endpoint requires the logged userId it adds the pair (loggedId, loggedUserId) if logged_user: if not auth_required: userId = check_session(allowed_roles) if userId != None: request_url_params_dict["loggedId"] = userId else: request_url_params_dict["loggedId"] = None # Convert the silence-style placeholders in the SQL query to proper MySQL placeholders query_string = silence_to_mysql(sql) # Default outputs res = None status = 200 # SELECT/GET operations if sql_op == SQL.SELECT: # The URL params have been checked to be enough to fill all SQL params url_pattern_params = tuple(request_url_params_dict[param] for param in sql_params) res = dal.api_safe_query(query_string, url_pattern_params) # Filter these results according to the URL query string, if there is one # Possible TO-DO: do this by directly editing the SQL query for extra efficiency res = filter_query_results(res, request.args) # In our teaching context, it is safe to assume that if the URL ends # with a parameter and we have no results, we should return a 404 code if RE_QUERY_PARAM.match(route) and not res: raise HTTPError(404, "Not found") else: # POST/PUT/DELETE operations #Construct a dict for all params expected in the request body, setting them to None if they have not been provided form = request.json if request.is_json else request.form body_params = { param: form.get(param, None) for param in request_body_params } # We have checked that sql_params is a subset of url_params U body_params, # construct a joint param object and use it to fill the SQL placeholders for param in url_params: body_params[param] = request_url_params_dict[param] if logged_user and auth_required: body_params["loggedId"] = userId param_tuple = tuple(body_params[param] for param in sql_params) param_tuple = tuple(body_params[param] for param in sql_params) # Run the execute query res = dal.api_safe_update(query_string, param_tuple) return jsonify(res), status