I am running docker container which contains REST API with tls enable, For which I am passing self signed .pem certificate and I am using reqwest crate to call the REST API. If tls is disable with container it works well. But when I run container with certificate, reqwest API call giving me error as mention below.Even after passing the same certificate from self-signed also giving me error.
error trying to connect: The certificate was not trusted."
reqwest = { version = "0.11.4", features = ["blocking", "json","native-tls"] }
async fn call_api<T: serde::Serialize>(
&self,
builder: ClientBuilder,
params: &T,
request_url: &str,
) -> Result<serde_json::Value> {
let mut headers = header::HeaderMap::new();
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_str(self.token.as_str()).unwrap(),
);
headers.insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
);
// read a local binary pem encoded certificate
let pem = std::fs::read("./cert.pem").unwrap();
let cert = reqwest::Certificate::from_pem(&pem)?;
let client = builder
.add_root_certificate(cert)
.default_headers(headers)
.build()?;
let response_json: serde_json::Value = client
.get(request_url)
.json(params)
.send()
.await?
.json()
.await?;
Ok(response_json)
}
Related
I have a Java application using the Microsoft Graph SDK to read from Azure AD. I am supposed to translate that application to Rust. In Java, i use code similar to this:
import com.azure.identity.ClientSecretCredentialBuilder;
import com.azure.identity.TokenCachePersistenceOptions;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.requests.GraphServiceClient;
import java.util.List;
public class GraphSdkClient {
public static void testApplicationClient() {
TokenCachePersistenceOptions options = new TokenCachePersistenceOptions()
.setName("ILM-Demo")
.setUnencryptedStorageAllowed(false);
ClientSecretCredentialBuilder builder = new ClientSecretCredentialBuilder()
.clientId("<MyClientId>")
.tenantId("<MyTenantId>")
.clientSecret("<MyClientSecret>")
.tokenCachePersistenceOptions(options);
TokenCredentialAuthProvider provider = new TokenCredentialAuthProvider(
List.of("Calendars.Read", "Calendars.ReadBasic.All"),
builder.build()
);
GraphServiceClient<?> client = GraphServiceClient
.builder()
.authenticationProvider(provider)
.buildClient();
client.me().calendar().calendarView().buildRequest().get();
}
}
It authenticates as an application, using only the client secret. The permission was given half a year ago and as long as the three values from the ClientSecretCredentialBuilder are correct, it works perfectly fine. Now i tried using a similar conecpt in Rust, taken from the graph-rs-sdk crate:
#[cfg(test)]
mod test {
use graph_rs_sdk::oauth::OAuth;
use warp::Filter;
use crate::{CLIENT_ID, CLIENT_SECRET, TENANT_ID};
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct ClientCredentialsResponse {
admin_consent: bool,
tenant: String,
}
#[tokio::test]
async fn ut_client_credentials() {
let query = warp::query::<ClientCredentialsResponse>()
.map(Some)
.or_else(|_| async {
Ok::<(Option<ClientCredentialsResponse>,), std::convert::Infallible>((None,))
});
let routes = warp::get()
.and(warp::path("redirect"))
.and(query)
.and_then(handle_redirect);
// Get the oauth client and request a browser sign in
let mut oauth = get_oauth_client();
let mut request = oauth.build_async().client_credentials();
request.browser_authorization().open().unwrap();
warp::serve(routes).run(([127, 0, 0, 1], 8300)).await;
}
async fn handle_redirect(client_credential_option: Option<ClientCredentialsResponse>)
-> Result<Box<dyn warp::Reply>, warp::Rejection> {
match client_credential_option {
Some(client_credential_response) => {
// Print out for debugging purposes.
println!("{:#?}", client_credential_response);
// Request an access token.
request_access_token().await;
// Generic login page response.
Ok(Box::new(
"Successfully Logged In! You can close your browser.",
))
}
None => Err(warp::reject()),
}
}
async fn request_access_token() {
let mut oauth = get_oauth_client();
let mut request = oauth.build_async().client_credentials();
let access_token = request.access_token().send().await.unwrap();
println!("{:#?}", access_token);
oauth.access_token(access_token);
}
fn get_oauth_client() -> OAuth {
let mut oauth = OAuth::new();
oauth
.client_id(CLIENT_ID)
.client_secret(CLIENT_SECRET)
.tenant_id(TENANT_ID)
.add_scope("https://graph.microsoft.com/User.Invite.All")
.redirect_uri("http://localhost:8300/redirect")
// .authorize_url("https://login.microsoftonline.com/common/adminconsent")
.access_token_url("https://login.microsoftonline.com/common/oauth2/v2.0/token");
oauth
}
}
Note that i commented out the authorize url. If this url exits, a browser window opens, requesting an admin to log in. This must not happen. When it is commented out, it sends the request directly to <tenantId>/oauth2/v2.0/authorize instead of <tenantId>/oauth2/v2.0/adminconsent, which is what i want, it instead complains: AADSTS900144: The request body must contain the following parameter: 'scope'.. The scope is given though.
I already tried fiddling with urls for hours and also tried every other authentication concept in the crate, but it doesn't seem to work without interaction, which is not possible in my use case. Does someone know how to achieve that? (All permissions are granted as application permissions, not deligated)
Edit request: Please create and add the tag "graph-rs-sdk". I cannot create it myself but knowing the crate being used would be useful.
I'm building a Tauri app and would like to set up OAuth integration with Google. To do so, I will need a URI for the oauth callback, but Tauri is unclear how to configure the schema possibly using this method or with the WindowUrl?
How can I add a URI to my Tauri app so I could like to it like the following example:
myapp://callback
I think it could look something like the following:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.register_uri_scheme_protocol("myapp", move |app, request| {
# protocol logic here
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Tauri currently doesn't directly support deep linking. A good alternative that I found was this rust project. After installation you can do something like the following:
#[tauri::command]
async fn start_oauth_server(window: Window) -> Result<u16, String> {
println!("Starting server");
start(None, move |url| {
// Because of the unprotected localhost port, you must verify the URL here.
// Preferebly send back only the token, or nothing at all if you can handle everything else in Rust.
// convert the string to a url
let url = url::Url::parse(&url).unwrap();
// get the code query parameter
let code = url
.query_pairs()
.find(|(k, _)| k == "code")
.unwrap_or_default()
.1;
// get the state query parameter
let state = url
.query_pairs()
.find(|(k, _)| k == "state")
.unwrap_or_default()
.1;
// create map of query parameters
let mut query_params = HashMap::new();
query_params.insert("code".to_string(), code.to_string());
query_params.insert("state".to_string(), state.to_string());
query_params.insert(String::from("redirect_uri"), url.to_string());
if window.emit("redirect_uri", query_params).is_ok() {
println!("Sent redirect_uri event");
} else {
println!("Failed to send redirect_uri event");
}
})
.map_err(|err| err.to_string())
}
I have a Rust application that is acting as a proxy. From the user perspective there is a web UI front end. This contains a button that when invoked will trigger a GET request to the Rust application. This in turn calls an external endpoint that returns the CSV file.
What I want is have the file download to the browser when the user clicks the button. Right now, the contents of the CSV file are returned to the browser rather than the file itself.
use std::net::SocketAddr;
use axum::{Router, Server};
use axum::extract::Json;
use axum::routing::get;
pub async fn downloadfile() -> Result<Json<String>, ApiError> {
let filename = ...;
let endpoint = "http://127.0.0.1:6101/api/CSV/DownloadFile";
let path = format!("{}?filename={}", endpoint, filename);
let response = reqwest::get(path).await?;
let content = response.text().await?;
Ok(Json(content))
}
pub async fn serve(listen_addr: SocketAddr) {
let app = Router::new()
.route("/downloadfile", get(downloadfile));
Server::bind(&listen_addr)
.serve(app.into_make_service())
.await
.unwrap();
}
I understand the reason I'm getting the contents is because I'm returning the content string as JSON. This makes sense. However, what would I need to change to return the file itself so the browser downloads it directly for the user?
I've managed to resolve it and now returns the CSV as a file. Here's the function that works:
use axum::response::Headers;
use http::header::{self, HeaderName};
pub async fn downloadfile() -> Result<(Headers<[(HeaderName, &'static str); 2]>, String), ApiError> {
let filename = ...;
let endpoint = "http://127.0.0.1:6101/api/CSV/DownloadFile";
let path = format!("{}?filename={}", endpoint, filename);
let response = reqwest::get(path).await?;
let content = response.text().await?;
let headers = Headers([
(header::CONTENT_TYPE, "text/csv; charset=utf-8"),
(header::CONTENT_DISPOSITION, "attachment; filename=\"data.csv\""),
]);
Ok((headers, content))
}
I'm still struggling with being able to set a dynamic file name, but that for another question.
I am using tokio rustls and I am getting Illegal SNI hostname received [49, 48, 46, 48, 46, 48, 46, 52] when hooking up a server to a legacy system.
This is my configuration:
let tls_cfg = {
// Load public certificate.
let server_cert = X509::stack_from_pem(server_cert.as_bytes()).unwrap();
let mut server_certs: Vec<Certificate> = Vec::new();
for x509 in server_cert {
let certificate = tokio_rustls::rustls::Certificate(x509.to_der().unwrap());
server_certs.push(certificate);
}
// Load private key.
let server_key = pem_parser::pem_to_der(server_key);
let server_key = tokio_rustls::rustls::PrivateKey(server_key);
// Do not use client certificate authentication.
let mut cfg = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(server_certs, server_key)
.unwrap();
// Configure ALPN to accept HTTP/2, HTTP/1.1 in that order.
//cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
sync::Arc::new(cfg)
};
let acceptor = tokio_rustls::TlsAcceptor::from(tls_cfg);
let client_stream = acceptor.accept_with(client_stream, session).await.unwrap();
Is there a way to ignore that illegal SNI message since I cannot change the code the generates it?
I'm trying to port a crypto exchange API from JavaScript to Rust. The JavaScript version authenticates and connects. The payload and secret key are signed using Hmac Sha256 and encoded to Base64. Below is the working implementation in JavaScript.
var param = "test";
var privateKey = "secret";
var signature = crypto.createHmac('sha256', new Buffer(privateKey, 'utf8'))
.update(param)
.digest('base64');
It returns the following signature: Aymga2LNFrM+tnkr6MYLFY2Jou46h2/Omogeu0iMCRQ=
In Rust i'm using Hmac SHA256 and encoding the result back to Base64 yet i'm unable to get close to the expected signature. Here is my best 2 day attempt at this:
extern crate ring;
extern crate data_encoding;
extern crate hex;
use ring::{digest, hmac};
use data_encoding::BASE64;
use hex::encode as hex_encode;
fn main() {
let secret_key = "secret";
let payload = "test";
let signed_key = hmac::SigningKey::new(&digest::SHA256, secret_key.as_bytes());
let signature = hex_encode(hmac::sign(&signed_key, payload.as_bytes()).as_ref());
let b64_encoded_sig = BASE64.encode(signature.as_ref());
assert_eq!(b64_encoded_sig, "Aymga2LNFrM+tnkr6MYLFY2Jou46h2/Omogeu0iMCRQ=");
}
This returns the following signature: MDMyOWEwNmI2MmNkMTZiMzNlYjY3OTJiZThjNjBiMTU4ZDg5YTJlZTNhODc2ZmNlOWE4ODFlYmI0ODhjMDkxNA==
Would really appreciate getting a point in the right direction, gone around in circles for days now and not sure where exactly i'm going wrong. Thanks.
Just remove the hex-thing
extern crate ring;
extern crate data_encoding;
use ring::{digest, hmac};
use data_encoding::BASE64;
fn main() {
let secret_key = "secret";
let payload = "test";
let signed_key = hmac::SigningKey::new(&digest::SHA256, secret_key.as_bytes());
let signature = hmac::sign(&signed_key, payload.as_bytes());
let b64_encoded_sig = BASE64.encode(signature.as_ref());
assert_eq!(b64_encoded_sig, "Aymga2LNFrM+tnkr6MYLFY2Jou46h2/Omogeu0iMCRQ=");
}