Greeneco
Greeneco

Reputation: 741

Rocket - Use state within my guards fails as traits are not implemented

I want to use state within my guards. I want to have routes that required authentication with an api key which I want to define in my Rocket.toml. But running this code I get the following error:

the trait From<(Status, ())> is not implemented for (Status, ApiKeyError)

for this line of code let config_state = try_outcome!(req.guard::<State<'_, Config>>().await);

How do I implement this trait? Or is there even a better solution to manage the api token in Rocket.

I am using the 0.5.0-devversion of Rocket.

#[macro_use] extern crate rocket;

use rocket::http::Status;
use rocket::request::{Outcome, Request, FromRequest};
use rocket::State;
use rocket::fairing::AdHoc;
use serde::Deserialize;

#[derive(Deserialize)]
struct Config {
    api_key: String,
}

struct ApiKey<'r>(&'r str);

#[derive(Debug)]
enum ApiKeyError {
    Missing,
    Invalid,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey<'r> {
    type Error = ApiKeyError;

    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {

        let config_state = try_outcome!(req.guard::<State<'_, Config>>().await);

        /// Returns true if `key` is a valid API key string.
        fn is_valid(key: &str, api_key: String) -> bool {
            key == api_key
        }

        match req.headers().get_one("Authorization") {
            None => Outcome::Failure((Status::Unauthorized, ApiKeyError::Missing)),
            Some(key) if is_valid(key, config_state.api_key) => Outcome::Success(ApiKey(key)),
            Some(_) => Outcome::Failure((Status::Unauthorized, ApiKeyError::Invalid)),
        }
    }
}

#[get("/")]
async fn index(config: State<'_, Config>, key: ApiKey<'_>) -> &'static str {
    "Hello, world!"
}

fn rocket() -> rocket::Rocket {
    let rocket = rocket::ignite();
    let figment = rocket.figment();

    let config: Config = figment.extract().expect("config");

    rocket
    .mount("/", routes![index])
    .attach(AdHoc::config::<Config>())
}

#[rocket::main]
async fn main() {
    rocket()
        .launch()
        .await;
}

Upvotes: 2

Views: 1807

Answers (1)

Greeneco
Greeneco

Reputation: 741

I already stored the config with AdHoch::config() but to retrieve it within the guard I need to use request.rocket().state::<Config>(). The corrected source code is below:

#[macro_use] extern crate rocket;

use rocket::http::Status;
use rocket::request::{Outcome, Request, FromRequest};
use rocket::State;
use rocket::fairing::AdHoc;
use serde::Deserialize;

#[derive(Deserialize)]
struct Config {
    api_key: String,
}

struct ApiKey<'r>(&'r str);

#[derive(Debug)]
enum ApiKeyError {
    Missing,
    Invalid,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey<'r> {
    type Error = ApiKeyError;

    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {

        // Retrieve the config state like this
        let config = req.rocket().state::<Config>().unwrap();

        /// Returns true if `key` is a valid API key string.
        fn is_valid(key: &str, api_key: &str) -> bool {
            key == api_key
        }

        match req.headers().get_one("Authorization") {
            None => Outcome::Failure((Status::Unauthorized, ApiKeyError::Missing)),
            Some(key) if is_valid(key, &config.api_key) => Outcome::Success(ApiKey(key)),
            Some(_) => Outcome::Failure((Status::Unauthorized, ApiKeyError::Invalid)),
        }
    }
}

#[get("/")]
async fn index(config: State<'_, Config>, key: ApiKey<'_>) -> &'static str {
    "Hello, world!"
}

fn rocket() -> rocket::Rocket {
    let rocket = rocket::ignite();
    let figment = rocket.figment();

    let config: Config = figment.extract().expect("config");

    rocket
    .mount("/", routes![index])
    .attach(AdHoc::config::<Config>())
}

#[rocket::main]
async fn main() {
    rocket()
        .launch()
        .await;
}

Upvotes: 2

Related Questions