celcat/
fetch.rs

1use lazy_static::lazy_static;
2use log::{debug, info};
3use regex::Regex;
4use serde::Serialize;
5use thiserror::Error;
6
7use crate::fetchable::Fetchable;
8
9#[derive(Error, Debug)]
10pub enum FetchError {
11    #[error("request error: {0}")]
12    Reqwest(#[from] reqwest::Error),
13    #[error("cannot find the token")]
14    Token,
15}
16
17#[derive(Debug, Clone)]
18pub struct Celcat {
19    client: reqwest::Client,
20    address: String,
21    token: String,
22}
23
24impl Celcat {
25    pub async fn new<S>(address: S) -> Result<Self, FetchError>
26    where
27        S: AsRef<str>,
28    {
29        let client = reqwest::Client::builder().cookie_store(true).build()?;
30
31        let token = Self::fetch_token(&client, address.as_ref()).await?;
32
33        Ok(Self {
34            client,
35            address: address.as_ref().to_owned(),
36            token,
37        })
38    }
39
40    async fn fetch_token(client: &reqwest::Client, address: &str) -> Result<String, FetchError> {
41        lazy_static! {
42            static ref RE: Regex =
43                Regex::new(r#"<input name="__RequestVerificationToken".*?value="([^"]+)""#)
44                    .unwrap();
45        }
46        info!("fetching celcat token");
47        let body = client
48            .get(&format!("{}/LdapLogin", address))
49            .send()
50            .await?
51            .text()
52            .await?;
53
54        if let Some(token) = RE.captures(&body).and_then(|caps| caps.get(1)) {
55            Ok(token.as_str().to_owned())
56        } else {
57            Err(FetchError::Token)
58        }
59    }
60
61    pub async fn login(&mut self, username: &str, password: &str) -> Result<(), FetchError> {
62        #[derive(Debug, Serialize)]
63        struct Form<'a> {
64            #[serde(rename = "Name")]
65            username: &'a str,
66            #[serde(rename = "Password")]
67            password: &'a str,
68            #[serde(rename = "__RequestVerificationToken")]
69            token: &'a str,
70        }
71
72        info!("fetching celcat federation ids");
73        let form = Form {
74            username,
75            password,
76            token: &self.token,
77        };
78        debug!("{:?}", form);
79        self.client
80            .post(&format!("{}/LdapLogin/Logon", self.address))
81            .form(&form)
82            .send()
83            .await?;
84
85        Ok(())
86    }
87
88    pub async fn fetch<F>(&self, req: F::Request) -> Result<F, FetchError>
89    where
90        F: Fetchable,
91    {
92        let res = self
93            .client
94            .post(&format!("{}/Home/{}", self.address, F::METHOD_NAME))
95            .form(&req)
96            .send()
97            .await?
98            .json()
99            .await?;
100
101        Ok(res)
102    }
103}