mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
137 lines
4.5 KiB
Rust
137 lines
4.5 KiB
Rust
use std::str::FromStr;
|
|
|
|
use kanidm_proto::constants::uri::{
|
|
OAUTH2_AUTHORISE, OAUTH2_AUTHORISE_DEVICE, OAUTH2_TOKEN_ENDPOINT,
|
|
};
|
|
use oauth2::basic::BasicClient;
|
|
use oauth2::devicecode::StandardDeviceAuthorizationResponse;
|
|
use oauth2::http::StatusCode;
|
|
use oauth2::{
|
|
AuthUrl, ClientId, DeviceAuthorizationUrl, HttpRequest, HttpResponse, Scope, TokenUrl,
|
|
};
|
|
use reqwest::Client;
|
|
use sketching::tracing_subscriber::layer::SubscriberExt;
|
|
use sketching::tracing_subscriber::util::SubscriberInitExt;
|
|
use sketching::tracing_subscriber::{fmt, EnvFilter};
|
|
use tracing::level_filters::LevelFilter;
|
|
use tracing::{debug, error, info};
|
|
|
|
async fn http_client(
|
|
request: HttpRequest,
|
|
) -> Result<HttpResponse, oauth2::reqwest::Error<reqwest::Error>> {
|
|
let client = Client::builder()
|
|
.danger_accept_invalid_certs(true)
|
|
// Following redirects opens the client up to SSRF vulnerabilities.
|
|
.redirect(reqwest::redirect::Policy::none())
|
|
.build()
|
|
.map_err(oauth2::reqwest::Error::Reqwest)?;
|
|
|
|
let method = reqwest::Method::from_str(request.method.as_str())
|
|
.map_err(|err| oauth2::reqwest::Error::Other(err.to_string()))?;
|
|
|
|
let mut request_builder = client
|
|
.request(method, request.url.as_str())
|
|
.body(request.body);
|
|
|
|
for (name, value) in &request.headers {
|
|
request_builder = request_builder.header(name.as_str(), value.as_bytes());
|
|
}
|
|
|
|
let response = client
|
|
.execute(request_builder.build().map_err(|err| {
|
|
error!("Failed to build request... {:?}", err);
|
|
oauth2::reqwest::Error::Reqwest(err)
|
|
})?)
|
|
.await
|
|
.map_err(|err| {
|
|
error!("Failed to query url {} error={:?}", request.url, err);
|
|
oauth2::reqwest::Error::Reqwest(err)
|
|
})?;
|
|
|
|
let status_code = StatusCode::from_u16(response.status().as_u16())
|
|
.map_err(|err| oauth2::reqwest::Error::Other(err.to_string()))?;
|
|
let headers = response
|
|
.headers()
|
|
.into_iter()
|
|
.map(|(k, v)| {
|
|
debug!("header key={:?} value={:?}", k, v);
|
|
(
|
|
oauth2::http::HeaderName::from_str(k.as_str()).expect("Failed to parse header"),
|
|
oauth2::http::HeaderValue::from_str(
|
|
v.to_str().expect("Failed to parse header value"),
|
|
)
|
|
.expect("Failed to parse header value"),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
let body = response.bytes().await.map_err(|err| {
|
|
error!("Failed to parse body...? {:?}", err);
|
|
oauth2::reqwest::Error::Reqwest(err)
|
|
})?;
|
|
info!("Response body: {:?}", String::from_utf8(body.to_vec()));
|
|
|
|
Ok(HttpResponse {
|
|
status_code,
|
|
headers,
|
|
body: body.to_vec(),
|
|
})
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
let fmt_layer = fmt::layer().with_writer(std::io::stderr);
|
|
|
|
let filter_layer = EnvFilter::builder()
|
|
.with_default_directive(LevelFilter::INFO.into())
|
|
.parse_lossy("info,kanidm_client=warn,kanidm_cli=info");
|
|
|
|
sketching::tracing_subscriber::registry()
|
|
.with(filter_layer)
|
|
.with(fmt_layer)
|
|
.init();
|
|
|
|
info!("building client...");
|
|
|
|
// kanidm system oauth2 create-public device_flow device_flow 'https://deviceauth'
|
|
let client = BasicClient::new(
|
|
ClientId::new("device_code".to_string()),
|
|
None,
|
|
AuthUrl::new(format!("https://localhost:8443{}", OAUTH2_AUTHORISE))?,
|
|
Some(TokenUrl::new(format!(
|
|
"https://localhost:8443{}",
|
|
OAUTH2_TOKEN_ENDPOINT
|
|
))?),
|
|
)
|
|
.set_device_authorization_url(DeviceAuthorizationUrl::new(format!(
|
|
"https://localhost:8443{}",
|
|
OAUTH2_AUTHORISE_DEVICE
|
|
))?);
|
|
|
|
info!("Getting details...");
|
|
|
|
let details: StandardDeviceAuthorizationResponse = client
|
|
.exchange_device_code()
|
|
.inspect_err(|err| error!("configuration error: {:?}", err))?
|
|
.add_scope(Scope::new("read".to_string()))
|
|
.request_async(http_client)
|
|
.await?;
|
|
|
|
println!(
|
|
"Open this URL in your browser: {}",
|
|
match details.verification_uri_complete() {
|
|
Some(uri) => uri.secret().as_str(),
|
|
None => details.verification_uri().as_str(),
|
|
}
|
|
);
|
|
|
|
println!("the code is {}", details.user_code().secret());
|
|
|
|
let token_result = client
|
|
.exchange_device_access_token(&details)
|
|
.request_async(http_client, tokio::time::sleep, None)
|
|
.await?;
|
|
println!("Result: {:?}", token_result);
|
|
Ok(())
|
|
}
|