1extern crate jsonwebtoken as jwt;
2
3use chrono::prelude::*;
4use jwt::{encode, Algorithm, Header};
5use openssl::rsa;
6use reqwest;
7use reqwest::header;
8use serde::{self, Deserialize, Serialize};
9use serde_json::value::Value;
10use std::collections::HashMap;
11use std::env;
12use std::fs;
13use log::{info, debug};
14
15const TOKEN_LIFETIME: i64 = 3600; #[derive(Serialize, Deserialize)]
20struct Claims {
21 iss: String,
22 scope: String,
23 aud: String,
24 exp: i64,
25 iat: i64,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 sub: Option<String>,
28}
29
30#[derive(Serialize, Deserialize)]
31struct SublessClaims {
32 iss: String,
33 scope: String,
34 aud: String,
35 exp: i64,
36 iat: i64,
37}
38
39#[derive(Debug, Serialize, Deserialize)]
40struct ServiceAccount {
41 #[serde(rename = "type")]
42 auth_type: String,
43 project_id: String,
44 private_key_id: String,
45 private_key: String,
46 client_email: String,
47 client_id: String,
48 auth_uri: String,
49 token_uri: String,
50 auth_provider_x509_cert_url: String,
51 client_x509_cert_url: String,
52}
53
54fn generate_jwt_string(user: Option<&str>, scope: &str) -> String {
56 let service_account_file = env::var("DRIVE_ADV_SERVICE_ACCOUNT").expect(
57 "DRIVE_ADV_SERVICE_ACCOUNT env variable not set. Please reference path to service account",
58 );
59 let service_account_file =
60 fs::File::open(service_account_file).expect("Service account file does not exist at path");
61 let service_account: ServiceAccount = serde_json::from_reader(service_account_file).unwrap();
62 let now = Utc::now().timestamp();
63 let mut scope_url: String = "https://www.googleapis.com/auth/".to_string();
64 scope_url.push_str(scope);
65 let sub_user = match user {
66 Some(u) => Some(u.to_string()),
67 None => None,
68 };
69 let claims = Claims {
70 iss: service_account.client_email,
71 scope: scope_url,
72 iat: now,
73 exp: now + TOKEN_LIFETIME, sub: sub_user,
75 aud: "https://oauth2.googleapis.com/token".to_string(),
76 };
77 let private_key =
78 rsa::Rsa::private_key_from_pem(service_account.private_key.as_bytes()).unwrap();
79 let mut header = Header::default();
80 header.alg = Algorithm::RS256;
81 let der = &private_key.private_key_to_der().unwrap();
82 let token = encode(&header, &claims, der).unwrap();
83 token
84}
85
86#[derive(Default, Debug)]
87pub struct AuthToken {
88 access_token: String, expiration: i64,
90 initialized: bool,
91}
92
93#[derive(Debug, Serialize, Deserialize)]
94pub struct OfflineToken {
95 access_token: String,
96 client_secret: String,
97 client_id: String,
98 refresh_token: String,
99}
100
101impl AuthToken {
102 pub fn get_token_string(&mut self, user: Option<&str>) -> String {
103 let now = Utc::now().timestamp();
104 if !self.initialized || now > self.expiration {
106 if let Some(path) = env::var("DRIVE_ADV_OFFLINE_OAUTH").ok() {
107 self.access_token = get_authentication_token_offline(&path);
108 }
109 else {
110 self.access_token = get_authentication_token(user);
111 }
112 self.expiration = now + TOKEN_LIFETIME / 2;
113 self.initialized = true;
114 } else {
115 debug!("Authentication token already exists")
116 }
117 self.access_token.clone() }
119}
120
121pub fn get_authentication_token_offline(path: &str) -> String {
122 let offline_token_file =
123 fs::File::open(path).expect("Service account file does not exist at path");
124 let offline_token: OfflineToken = serde_json::from_reader(offline_token_file).unwrap();
125 let access_token_url = "https://oauth2.googleapis.com/token";
126 let client = reqwest::blocking::Client::new();
127 let body = format!("client_id={}&client_secret={}&refresh_token={}&grant_type=refresh_token", offline_token.client_id, offline_token.client_secret, offline_token.refresh_token);
128
129 let res = client
130 .post(access_token_url)
131 .header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
132 .body(body)
133 .send()
134 .unwrap();
135 let result_json: Value = res.json().unwrap();
136 return result_json.get("access_token").unwrap().to_string();
137}
138
139pub fn get_authentication_token(user: Option<&str>) -> String {
141 debug!("Getting authentication token");
142 let scope = env::var("DRIVE_SCOPE").expect("DRIVE_SCOPE not set. Should be drive or drive.readonly");
143 let access_token_url = "https://oauth2.googleapis.com/token";
144 let jwt_string = generate_jwt_string(user, &scope);
145 let client = reqwest::blocking::Client::new();
146 let body = format!(
147 "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={}",
148 jwt_string
149 ); let res = client
151 .post(access_token_url)
152 .header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
153 .body(body)
154 .send()
155 .unwrap();
156 let result_json: Value = res.json().unwrap();
157 return result_json.get("access_token").unwrap().to_string()
158}