1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
use std::io::Read; use std::sync::mpsc::{channel, Sender}; use reqwest::header::USER_AGENT; use threadpool::ThreadPool; use crate::build_request; use crate::config::Config; use crate::yubicoerror::YubicoError; use crate::Request; use crate::Result; use reqwest::blocking::Client; use std::sync::Arc; pub fn verify<S>(otp: S, config: Config) -> Result<String> where S: Into<String>, { Verifier::new(config)?.verify(otp) } pub struct Verifier { config: Config, thread_pool: ThreadPool, client: Arc<Client>, } impl Verifier { pub fn new(config: Config) -> Result<Verifier> { let number_of_hosts = config.api_hosts.len(); let client = if config.proxy_url != "" && config.proxy_username == "" { Verifier::get_client_proxy(config.clone())? } else if config.proxy_url != "" && config.proxy_username != "" { Verifier::get_client_proxy_with_auth(config.clone())? } else { Client::builder().timeout(config.request_timeout).build()? }; Ok(Verifier { config, thread_pool: ThreadPool::new(number_of_hosts), client: Arc::new(client), }) } pub fn verify<S>(&self, otp: S) -> Result<String> where S: Into<String>, { let request = build_request(otp, &self.config)?; let number_of_hosts = self.config.api_hosts.len(); let (tx, rx) = channel(); for api_host in &self.config.api_hosts { let tx = tx.clone(); let request = request.clone(); let url = request.build_url(api_host); let user_agent = self.config.user_agent.to_string(); let client = self.client.clone(); self.thread_pool.execute(move || { process(&client, tx, url, &request, user_agent); }); } let mut success = false; let mut results: Vec<Result<String>> = Vec::new(); for _ in 0..number_of_hosts { match rx.recv() { Ok(Response::Signal(result)) => match result { Ok(_) => { results.truncate(0); success = true; } Err(_) => { results.push(result); } }, Err(e) => { results.push(Err(YubicoError::ChannelError(e))); break; } } } if success { Ok("The OTP is valid.".into()) } else { results.pop().ok_or_else(|| YubicoError::InvalidOtp)? } } fn get_client_proxy(config: Config) -> Result<Client> { Ok(Client::builder() .timeout(config.request_timeout) .proxy(reqwest::Proxy::all(&config.proxy_url)?).build()?) } fn get_client_proxy_with_auth(config: Config) -> Result<Client> { let proxy = reqwest::Proxy::all(&config.proxy_url)? .basic_auth(&config.proxy_username, &config.proxy_password); Ok(Client::builder().timeout(config.request_timeout).proxy(proxy).build()?) } } enum Response { Signal(Result<String>), } fn process( client: &Client, sender: Sender<Response>, url: String, request: &Request, user_agent: String, ) { match get(client, url, user_agent) { Ok(raw_response) => { let result = request .response_verifier .verify_response(raw_response) .map(|()| "The OTP is valid.".to_owned()); sender.send(Response::Signal(result)).unwrap(); } Err(e) => { sender.send(Response::Signal(Err(e))).unwrap(); } } } pub fn get(client: &Client, url: String, user_agent: String) -> Result<String> { let mut response = client .get(url.as_str()) .header(USER_AGENT, user_agent) .send()?; let mut data = String::new(); response.read_to_string(&mut data)?; Ok(data) }