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)
}