Convert rocket_contrib::Json into bytes

Hi,

I'm trying to implement in rocket 0.4 a Webhook, and the header will contains a encrypted key.
I have to validate that key given datas, and I have to use the payload.

But I do not find how to convert Json into bytes.

#[post("/webhook/<property_id>", data = "<body>")]
fn t(property_id: &RawStr, body: Json<WebhookBody>, x_hook_signature: XHookSignature) {
    x_hook_signature.validate(b"test", body.as_bytes());
}

pub struct XHookSignature(String);

impl XHookSignature {
    pub fn validate(&self, key: &[u8], message: &[u8]) -> bool {
        let hash = // encryption method with key and message that return '&[u8]';
        self.0 == hash.iter().map(|b| format!("{:02x}", b)).collect::<Vec<_>>().join("")
    }
}

So the body.as_byte() does not work, and I found nothing in rocket_contrib documentation that can help me...

Have you any idea of the way to do that please ?

You shouldn't use the Json<T> utility if you need to see the raw bytes. Instead, use a body like rocket::data::Data to get the raw bytes, validate them, then call serde directly to have the byte array converted into a WebhookBody object.

2 Likes

That's a good idea ! :smiley:

But I get a problem... The data is empty.
But with Postman I give a value :

image

Here's the logs :

DEBUG application::request - Decrypted mess : test
DEBUG application::request - Decrypted data : 
DEBUG application::request - Hook signature : 7a7942582ceb81052b01fc52d05ac4d431f8e7db
DEBUG application::request - Encrypted data : fc85087452696e5bcbe3b7a71fde00e320af2cca

And the code :

#[post("/webhook/<agency_id>", data = "<body>")]
fn webhook(agency_id: &RawStr, body: Data, x_hook_signature: XHookSignature) -> Result<ApiResponse<()>, ApiResponse<ApiError>> {
    let mut datas = vec![];
    while !body.peek_complete() {
        datas.extend_from_slice(body.peek());
    }

    if !x_hook_signature.validate(agency_id.as_bytes(), &datas[..]) {
        return Err(ApiResponse::new(Status::InternalServerError, ApiError::new("Cannot validate hook signature with given datas !")));
    }

    let json_body: Json<WebhookBody>;
    // Json::from_data(_, o: Transformed<'a, Self>);
    // Json::transform(r: &Request, d: Data);

    Ok(ApiResponse::new(Status::Ok, ()))
}

pub struct XHookSignature(String);

impl XHookSignature {
    pub fn validate(&self, key: &[u8], message: &[u8]) -> bool {
        log::debug!("Decrypted mess : {}", std::str::from_utf8(key).unwrap_or("failed to parse"));
        log::debug!("Decrypted data : {}", std::str::from_utf8(message).unwrap_or("failed to parse"));
        let hash = encryption method with key and message that return '&[u8]' and parsed into String;
        log::debug!("Hook signature : {}", self.0);
        log::debug!("Encrypted data : {}", hash);
        self.0 == hash
    }
}

Did I do something wrong ?

I don't think you're supposed to use Data in that way. Try this instead:

let mut datas = Vec::new();
body.stream_to(&mut datas);

Oh yeah in that way that's working ! Thank you :slight_smile:

But about converting data into Json, I've found only two functions, and they need Request.
Do I have to use it or are there another way, like let json_body: Json<WebhookBody> = Json(std::str::from_utf8(message).unwrap_or_default()); or something like that ?

Just use the serde_json crate directly instead of using the rocket Json type. The serde_json::from_slice function can do the conversion from a Vec<u8> to your WebhookBody type.

Thank you very much for your help ! :smiley:

Why don't I think the same way as you ?
Why do I always want to make it more complicated ?

For who need the implementation, here it is :

#[post("/webhook/<agency_id>", data = "<data_body>")]
fn webhook(
    agency_id: &RawStr,
    data_body: Data,
    x_hook_signature: XHookSignature,
    api_state: State<ApiState>,
) -> Result<ApiResponse<()>, ApiResponse<ApiError>> {
    let mut datas = vec![];
    if let Err(e) = data_body.stream_to(&mut datas) {
        return Err(ApiResponse::new(
            Status::InternalServerError,
            ApiError::new(format!("Cannot parse given datas: {}", e)),
        ));
    }

    if !x_hook_signature.validate(agency_id.as_bytes(), &datas[..]) {
        return Err(ApiResponse::new(
            Status::InternalServerError,
            ApiError::new("Cannot validate hook signature with given datas !"),
        ));
    }

    let body: WebhookBody = match serde_json::from_slice(&datas[..]) {
        Ok(j) => j,
        Err(e) => return Err(ApiResponse::new(
            Status::InternalServerError,
            ApiError::new(format!("Cannot parse datas as JSON object : {}", e)),
        ))
    };

    Ok(())
}