rustrust-warp

Warp filter moving variable out of its environment


I am trying to implement a filter that sits in all my routes and extracts a header and matches a possible token to what is stored on my system.

I want to implement something like the warp rejection example but I get the error

expected a closure that implements the Fn trait, but this closure only implements FnOnce closure is FnOnce because it moves the variable tmp out of its environment

I kind of get what the compiler saying, but don't know how to solve it. I thought doing let tmp = store.clone() would.

I have the filter:

pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {

   let tmp = store.clone();

    warp::header("Authorization").and_then (|auth_header: String| async move {

        // Authorization: BEARER authToken=xxxyyyzzz
        let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {

        if result.is_err() {
            return Err(reject::custom(HayStackAuthToken));
        }

        let (_, key_value) = result.unwrap();

        let auth_token_result = tmp.read().get_authtoken();

        if auth_token_result.is_err() {
            return Err(reject::custom(HayStackAuthToken));
        }

        let auth_token_option = auth_token_result.unwrap();

        if auth_token_option.is_none() {
            return Err(reject::custom(HayStackAuthToken));
        }

        let auth_token = auth_token_option.unwrap();

        if auth_token != key_value.1 {
            return Err(reject::custom(HayStackAuthToken));
        }

        Ok(tmp)
    })
}

store is type Store = Arc<RwLock<Box<dyn UserAuthStore>>> and UserAuthStore is trait UserAuthStore: fmt::Debug + Send + Sync.

UserAuthStore is defined as

pub trait UserAuthStore: fmt::Debug + Send + Sync {

    // Return handshake token for username. If user has no handshake token generate one
    fn get_handshake_token(&self, username: &str) -> HaystackResult<String>;
    fn get_username(&self, handshake_token: &str) -> HaystackResult<String>;

    fn set_temporary_value(&mut self, k: &str, v: &str) -> HaystackResult<()>;
    fn get_temporary_value(&self,  k: &str) -> HaystackResult<Option<&String>>;

    fn set_authtoken(&mut self, s: String) -> HaystackResult<()>;

    /// returns a base64 encoded sha256 salt of password.
    fn get_password_salt(&self) -> HaystackResult<String>;
    fn get_salted_password(&self) -> HaystackResult<String>;
    fn get_authtoken(&self) -> HaystackResult<Option<String>>;
}

Why does clone not work here?

The full error is

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
    --> src/server/mod.rs:997:45
     |
997  |       warp::header("Authorization").and_then (|auth_header: String| async move {
     |  ___________________________________--------__^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_-
     | |                                   |         |
     | |                                   |         this closure implements `FnOnce`, not `Fn`
     | |                                   the requirement to implement `Fn` derives from here
998  | |
999  | |         // Authorization: BEARER authToken=xxxyyyzzz
1000 | |         let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
...    |
1026 | |         Ok(tmp.clone())
1027 | |     })
     | |_____- closure is `FnOnce` because it moves the variable `tmp` out of its environment

You can see a simplified test case here


Solution

  • After creating a simple test case I managed to get it going with the following function.

    pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
    
        warp::header("Authorization").and_then (
            
                move |auth_header: String| 
                {
                    let tmp = store.clone();
                    async move {
    
                        let tmp = tmp.clone();
    
                        if tmp.read().get_authtoken().is_none() {
                            return Err(reject::custom(HayStackAuthToken));   
                        }
                        
                        Ok(tmp.clone())
                    }
                }
        )
    }
    

    So in the end just needed clone in the correct place.