openidconnect 3.0.0-alpha.1

OpenID Connect library
Documentation
#![allow(clippy::cognitive_complexity, clippy::expect_fun_call)]
extern crate color_backtrace;
extern crate env_logger;

use std::cell::RefCell;
use std::sync::Once;
use std::time::Duration;

use openidconnect::core::{
    CoreApplicationType, CoreClientRegistrationRequest, CoreClientRegistrationResponse,
    CoreProviderMetadata,
};
use openidconnect::{
    ClientContactEmail, ClientName, HttpRequest, HttpResponse, IssuerUrl, RedirectUrl,
};

pub const CERTIFICATION_BASE_URL: &str = "https://rp.certification.openid.net:8080";
pub const RP_CONTACT_EMAIL: &str = "ramos@cs.stanford.edu";
pub const RP_NAME: &str = "openidconnect-rs";
pub const RP_REDIRECT_URI: &str = "http://localhost:8080";

static INIT_LOG: Once = Once::new();

thread_local! {
    static TEST_ID: RefCell<&'static str> = RefCell::new("UNINITIALIZED_TEST_ID");
}

pub fn get_test_id() -> &'static str {
    TEST_ID.with(|id| *id.borrow())
}

pub fn set_test_id(test_id: &'static str) {
    TEST_ID.with(|id| *id.borrow_mut() = test_id);
}

#[macro_export]
macro_rules! log_error {
    ($($args:tt)+) => {
        error!("[{}] {}", rp_common::get_test_id(), format!($($args)+))
    }
}
#[macro_export]
macro_rules! log_info {
    ($($args:tt)+) => {
        info!("[{}] {}", rp_common::get_test_id(), format!($($args)+));
    }
}
#[macro_export]
macro_rules! log_debug {
    ($($args:tt)+) => {
        debug!("[{}] {}", rp_common::get_test_id(), format!($($args)+));
    }
}

#[macro_export]
macro_rules! log_container_field {
    ($container:ident. $field:ident) => {
        log_info!(
            concat!("  ", stringify!($field), " = {:?}"),
            $container.$field()
        );
    };
}

fn _init_log() {
    color_backtrace::install();
    env_logger::init();
}

pub fn init_log(test_id: &'static str) {
    INIT_LOG.call_once(_init_log);
    set_test_id(test_id);
}

pub fn http_client(
    request: HttpRequest,
) -> Result<HttpResponse, openidconnect::reqwest::HttpClientError> {
    retry::retry(
        (0..5).map(|i| {
            if i != 0 {
                warn!("Retrying HTTP request ({}/5)", i + 1)
            }
            Duration::from_millis(500)
        }),
        || openidconnect::reqwest::http_client(request.clone()),
    )
    .map_err(|err| match err {
        retry::Error::Operation { error, .. } => error,
        retry::Error::Internal(msg) => openidconnect::reqwest::Error::Other(msg),
    })
}

pub trait PanicIfFail<T, F>
where
    F: std::error::Error,
{
    fn panic_if_fail(self, msg: &'static str) -> T;
}
impl<T, F> PanicIfFail<T, F> for Result<T, F>
where
    F: std::error::Error,
{
    fn panic_if_fail(self, msg: &'static str) -> T {
        match self {
            Ok(ret) => ret,
            Err(fail) => {
                let mut err_msg = format!("Panic: {}", msg);

                let mut cur_fail: Option<&dyn std::error::Error> = Some(&fail);
                while let Some(cause) = cur_fail {
                    err_msg += &format!("\n    caused by: {}", cause);
                    cur_fail = cause.source();
                }
                error!("[{}] {}", get_test_id(), err_msg);
                panic!("{}", msg);
            }
        }
    }
}

pub fn issuer_url(test_id: &str) -> IssuerUrl {
    IssuerUrl::new(format!(
        "{}/{}/{}",
        CERTIFICATION_BASE_URL, RP_NAME, test_id
    ))
    .expect("Failed to parse issuer URL")
}

pub fn get_provider_metadata(test_id: &str) -> CoreProviderMetadata {
    let _issuer_url = issuer_url(test_id);
    CoreProviderMetadata::discover(&_issuer_url, http_client).expect(&format!(
        "Failed to fetch provider metadata from {:?}",
        _issuer_url
    ))
}

pub fn register_client<F>(
    provider_metadata: &CoreProviderMetadata,
    request_fn: F,
) -> CoreClientRegistrationResponse
where
    F: FnOnce(CoreClientRegistrationRequest) -> CoreClientRegistrationRequest,
{
    let registration_request_pre = CoreClientRegistrationRequest::new(
        vec![RedirectUrl::new(RP_REDIRECT_URI.to_string()).unwrap()],
        Default::default(),
    )
    .set_application_type(Some(CoreApplicationType::Native))
    .set_client_name(Some(
        vec![(None, ClientName::new(RP_NAME.to_string()))]
            .into_iter()
            .collect(),
    ))
    .set_contacts(Some(vec![ClientContactEmail::new(
        RP_CONTACT_EMAIL.to_string(),
    )]));

    let registration_request_post = request_fn(registration_request_pre);

    let registration_endpoint = provider_metadata
        .registration_endpoint()
        .expect("provider does not support dynamic registration");
    registration_request_post
        .register(registration_endpoint, http_client)
        .expect(&format!(
            "Failed to register client at {:?}",
            registration_endpoint
        ))
}