From f402d18bf6957066d9ccf0ee8e70e39763717b8f Mon Sep 17 00:00:00 2001 From: NathanosDev Date: Wed, 18 Sep 2024 21:32:17 +0200 Subject: [PATCH] docs(ic-http-certification): improve naming in example project --- .gitignore | 1 - Cargo.lock | 9 ++++ .../custom-assets/README.md | 42 +++++++++---------- .../custom-assets/src/backend/src/lib.rs | 34 +++++++-------- .../custom-assets/src/tests/src/http.spec.ts | 2 +- .../skip-certification/README.md | 8 +++- .../src/utils/skip_certification.rs | 27 ++++++++++-- 7 files changed, 78 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 6dfc9121..eee3d971 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ node_modules .DS_Store /tmp pkg -Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index ee34e536..7324222e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1098,6 +1098,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "http_certification_skip_certification_backend" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk", + "ic-http-certification", +] + [[package]] name = "httparse" version = "1.9.4" diff --git a/examples/http-certification/custom-assets/README.md b/examples/http-certification/custom-assets/README.md index 33c869f1..aa6db262 100644 --- a/examples/http-certification/custom-assets/README.md +++ b/examples/http-certification/custom-assets/README.md @@ -144,7 +144,7 @@ The first step is defining a reusable function to create a response with all of ```rust fn get_asset_headers( additional_headers: Vec, - body: &[u8], + content_length: usize, cel_expr: String, ) -> Vec<(String, String)> { // set up the default headers and include additional headers provided by the caller @@ -157,7 +157,7 @@ fn get_asset_headers( ("permissions-policy".to_string(), "accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()".to_string()), ("cross-origin-embedder-policy".to_string(), "require-corp".to_string()), ("cross-origin-opener-policy".to_string(), "same-origin".to_string()), - ("content-length".to_string(), body.len().to_string()), + ("content-length".to_string(), content_length.to_string()), (CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(), cel_expr), ]; headers.extend(additional_headers); @@ -170,7 +170,7 @@ fn create_asset_response( body: &[u8], cel_expr: String, ) -> HttpResponse { - let headers = get_asset_headers(additional_headers, body, cel_expr); + let headers = get_asset_headers(additional_headers, body.len(), cel_expr); HttpResponse::builder() .with_status_code(200) @@ -386,19 +386,19 @@ fn certify_index_asset() { } ``` -It's also possible to skip certification for certain routes. This can be relatively easily as follows: +It's also possible to skip certification for certain routes. This can be useful for scenarios where it's difficult to predict what the response will look like for a certain route and the content is not very security sensitive. This can be done as follows: ```rust -const DYNAMIC_REQ_PATH: &str = "/dynamic"; +const UNCERTIFIED_REQ_PATH: &str = "/uncertified"; -fn certify_dynamic_asset() { - let dynamic_req_tree_path = HttpCertificationPath::exact(DYNAMIC_REQ_PATH); - let dynamic_req_certification = HttpCertification::skip(); +fn add_certification_skips() { + let uncertified_req_tree_path = HttpCertificationPath::exact(UNCERTIFIED_REQ_PATH); + let uncertified_req_certification = HttpCertification::skip(); HTTP_TREE.with_borrow_mut(|http_tree| { http_tree.insert(&HttpCertificationTreeEntry::new( - dynamic_req_tree_path, - &dynamic_req_certification, + uncertified_req_tree_path, + &uncertified_req_certification, )); }); } @@ -418,7 +418,7 @@ With all of the above functions, it is now possible to certify all of the fronte ```rust fn certify_all_assets() { - certify_dynamic_asset(); + add_certification_skips(); certify_index_asset(); certify_asset_glob("assets/**/*.css", "text/css"); @@ -434,8 +434,8 @@ fn certify_all_assets() { With all assets certified, they can be served over HTTP. The steps to follow when serving assets are: -- Check if the request path matches the dynamic path. - - If the requested path exactly matches the dynamic path, serve the dynamic response. +- Check if the request path matches the uncertified path. + - If the requested path exactly matches the uncertified path, serve the uncertified response. - Check if the requested path matches a file (e.g., `/assets/app.js`). - If the request path exactly matches an existing file, serve that file. - Otherwise, serve the `index.html` file. @@ -452,13 +452,13 @@ fn asset_handler(req: &HttpRequest) -> HttpResponse<'static> { RESPONSES.with_borrow(|responses| { ENCODED_RESPONSES.with_borrow(|encoded_responses| { let (asset_req_path, asset_tree_path, identity_response) = - // if the request path matches the dynamic response's path, serve that - if req_path == DYNAMIC_REQ_PATH { + // if the request path matches the uncertified response's path, serve that + if req_path == UNCERTIFIED_REQ_PATH { ( - DYNAMIC_REQ_PATH.to_string(), - HttpCertificationPath::exact(DYNAMIC_REQ_PATH), + UNCERTIFIED_REQ_PATH.to_string(), + HttpCertificationPath::exact(UNCERTIFIED_REQ_PATH), CertifiedHttpResponse { - response: create_dynamic_response(), + response: create_uncertified_response(), certification: HttpCertification::skip(), }, ) @@ -538,10 +538,10 @@ fn asset_handler(req: &HttpRequest) -> HttpResponse<'static> { } ``` -Creating the dynamic response is done as follows: +Creating the uncertified response is done as follows: ```rust -fn create_dynamic_response() -> HttpResponse<'static> { +fn create_uncertified_response() -> HttpResponse<'static> { let body = format!( r#" @@ -564,7 +564,7 @@ fn create_dynamic_response() -> HttpResponse<'static> { let headers = get_asset_headers( additional_headers, - &body, + body.len(), DefaultCelBuilder::skip_certification().to_string(), ); diff --git a/examples/http-certification/custom-assets/src/backend/src/lib.rs b/examples/http-certification/custom-assets/src/backend/src/lib.rs index 2b05df47..2ff4b410 100644 --- a/examples/http-certification/custom-assets/src/backend/src/lib.rs +++ b/examples/http-certification/custom-assets/src/backend/src/lib.rs @@ -74,7 +74,7 @@ fn prepare_cel_exprs() { } fn certify_all_assets() { - certify_dynamic_asset(); + add_certification_skips(); certify_index_asset(); certify_asset_glob("assets/**/*.css", "text/css"); @@ -85,16 +85,16 @@ fn certify_all_assets() { update_certified_data(); } -const DYNAMIC_REQ_PATH: &str = "/dynamic"; +const UNCERTIFIED_REQ_PATH: &str = "/uncertified"; -fn certify_dynamic_asset() { - let dynamic_req_tree_path = HttpCertificationPath::exact(DYNAMIC_REQ_PATH); - let dynamic_req_certification = HttpCertification::skip(); +fn add_certification_skips() { + let uncertified_req_tree_path = HttpCertificationPath::exact(UNCERTIFIED_REQ_PATH); + let uncertified_req_certification = HttpCertification::skip(); HTTP_TREE.with_borrow_mut(|http_tree| { http_tree.insert(&HttpCertificationTreeEntry::new( - dynamic_req_tree_path, - &dynamic_req_certification, + uncertified_req_tree_path, + &uncertified_req_certification, )); }); } @@ -286,13 +286,13 @@ fn asset_handler(req: &HttpRequest) -> HttpResponse<'static> { RESPONSES.with_borrow(|responses| { ENCODED_RESPONSES.with_borrow(|encoded_responses| { let (asset_req_path, asset_tree_path, identity_response) = - // if the request path matches the dynamic response's path, serve that - if req_path == DYNAMIC_REQ_PATH { + // if the request path matches the uncertified response's path, serve that + if req_path == UNCERTIFIED_REQ_PATH { ( - DYNAMIC_REQ_PATH.to_string(), - HttpCertificationPath::exact(DYNAMIC_REQ_PATH), + UNCERTIFIED_REQ_PATH.to_string(), + HttpCertificationPath::exact(UNCERTIFIED_REQ_PATH), CertifiedHttpResponse { - response: create_dynamic_response(), + response: create_uncertified_response(), certification: HttpCertification::skip(), }, ) @@ -371,7 +371,7 @@ fn asset_handler(req: &HttpRequest) -> HttpResponse<'static> { }) } -fn create_dynamic_response() -> HttpResponse<'static> { +fn create_uncertified_response() -> HttpResponse<'static> { let body = format!( r#" @@ -394,7 +394,7 @@ fn create_dynamic_response() -> HttpResponse<'static> { let headers = get_asset_headers( additional_headers, - &body, + body.len(), DefaultCelBuilder::skip_certification().to_string(), ); @@ -407,7 +407,7 @@ fn create_dynamic_response() -> HttpResponse<'static> { fn get_asset_headers( additional_headers: Vec, - body: &[u8], + content_length: usize, cel_expr: String, ) -> Vec<(String, String)> { // set up the default headers and include additional headers provided by the caller @@ -420,7 +420,7 @@ fn get_asset_headers( ("permissions-policy".to_string(), "accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()".to_string()), ("cross-origin-embedder-policy".to_string(), "require-corp".to_string()), ("cross-origin-opener-policy".to_string(), "same-origin".to_string()), - ("content-length".to_string(), body.len().to_string()), + ("content-length".to_string(), content_length.to_string()), (CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(), cel_expr), ]; headers.extend(additional_headers); @@ -433,7 +433,7 @@ fn create_asset_response( body: &[u8], cel_expr: String, ) -> HttpResponse { - let headers = get_asset_headers(additional_headers, body, cel_expr); + let headers = get_asset_headers(additional_headers, body.len(), cel_expr); HttpResponse::builder() .with_status_code(200) diff --git a/examples/http-certification/custom-assets/src/tests/src/http.spec.ts b/examples/http-certification/custom-assets/src/tests/src/http.spec.ts index dc3d62d7..edaa49df 100644 --- a/examples/http-certification/custom-assets/src/tests/src/http.spec.ts +++ b/examples/http-certification/custom-assets/src/tests/src/http.spec.ts @@ -41,7 +41,7 @@ describe('HTTP', () => { it('should successfully skip verification', async () => { const request: Request = { - url: '/dynamic', + url: '/uncertified', method: 'GET', headers: [], body: new Uint8Array(), diff --git a/examples/http-certification/skip-certification/README.md b/examples/http-certification/skip-certification/README.md index a4b9abfa..6d7ff55b 100644 --- a/examples/http-certification/skip-certification/README.md +++ b/examples/http-certification/skip-certification/README.md @@ -18,11 +18,14 @@ This is a relatively simple guide so there's no prerequisites as such, but it's # Skipping certification -Skipping certification for all responses is a relatively simple task that can be completed in 2 very simple steps. +Skipping certification for all responses is a relatively simple task that can be completed in 2 steps. First, set the canister's certified data in the canister's `init` lifecycle hook: ```rust +use ic_cdk::*; +use ic_http_certification::utils::skip_certification_certified_data; + #[init] fn init() { set_certified_data(&skip_certification_certified_data()); @@ -34,6 +37,9 @@ This will make sure that the correct certified data is set so that it can be sig Next, when responding to HTTP requests, add the certificate header that will instruct the HTTP Gateway to skip verification: ```rust +use ic_cdk::{api::data_certificate, *}; +use ic_http_certification::utils::add_skip_certification_header; + #[query] fn http_request() -> HttpResponse<'static> { let mut response = create_response(); diff --git a/packages/ic-http-certification/src/utils/skip_certification.rs b/packages/ic-http-certification/src/utils/skip_certification.rs index df6c2354..31c6280e 100644 --- a/packages/ic-http-certification/src/utils/skip_certification.rs +++ b/packages/ic-http-certification/src/utils/skip_certification.rs @@ -1,4 +1,4 @@ -use super::add_certificate_header; +use super::add_v2_certificate_header; use crate::{ DefaultCelBuilder, Hash, HttpCertificationPath, HttpResponse, CERTIFICATE_EXPRESSION_HEADER_NAME, @@ -26,6 +26,7 @@ use ic_representation_independent_hash::hash; /// /// let mut response = HttpResponse::builder().build(); /// +/// // this should normally be retrieved using `ic_cdk::api::data_certificate()`. /// let data_certificate = vec![1, 2, 3]; /// /// add_skip_certification_header(data_certificate, &mut response); @@ -45,8 +46,8 @@ use ic_representation_independent_hash::hash; /// ); /// ``` pub fn add_skip_certification_header(data_certificate: Vec, response: &mut HttpResponse) { - add_certificate_header( - data_certificate, + add_v2_certificate_header( + &data_certificate, response, &skip_certification_asset_tree(), &HttpCertificationPath::wildcard("").to_expr_path(), @@ -68,7 +69,7 @@ pub fn add_skip_certification_header(data_certificate: Vec, response: &mut H /// /// let certified_data = skip_certification_certified_data(); /// -/// set_certified_date(&certified_data); +/// set_certified_data(&certified_data); /// ``` pub fn skip_certification_certified_data() -> Hash { skip_certification_asset_tree().digest() @@ -86,3 +87,21 @@ fn skip_certification_asset_tree() -> HashTree { labeled("<*>", labeled(cel_expr_hash, leaf(vec![]))), ) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_skip_certification_certified_data() { + let certified_data = skip_certification_certified_data(); + + assert_eq!( + certified_data, + [ + 85, 236, 195, 28, 62, 128, 71, 252, 21, 143, 32, 234, 10, 160, 96, 154, 172, 199, + 181, 126, 6, 234, 64, 220, 65, 134, 2, 114, 167, 214, 66, 145 + ] + ); + } +}