|
1 | 1 | //! Service responsible for authenticating with the cache with Oauth tokens.
|
2 | 2 | //! This authenticator periodically fetches a new token every set amount of seconds.
|
3 |
| -use std::sync::Arc; |
4 |
| - |
5 | 3 | use crate::config::BuilderConfig;
|
6 | 4 | use oauth2::{
|
7 | 5 | AuthUrl, ClientId, ClientSecret, EmptyExtraTokenFields, StandardTokenResponse, TokenUrl,
|
8 | 6 | basic::{BasicClient, BasicTokenType},
|
9 | 7 | reqwest::async_http_client,
|
10 | 8 | };
|
11 |
| -use tokio::{sync::RwLock, task::JoinHandle}; |
| 9 | +use std::sync::{Arc, Mutex}; |
| 10 | +use tokio::task::JoinHandle; |
12 | 11 |
|
13 | 12 | type Token = StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>;
|
14 | 13 |
|
15 |
| -/// A self-refreshing, periodically fetching authenticator for the block builder. |
16 |
| -/// It is architected as a shareable struct that can be used across all the multiple builder tasks. |
17 |
| -/// It fetches a new token every set amount of seconds, configured through the general builder config. |
18 |
| -/// Readers are guaranteed to not read stale tokens as the [RwLock] guarantees that write tasks (refreshing the token) will claim priority over read access. |
19 |
| -#[derive(Debug, Clone)] |
20 |
| -pub struct Authenticator { |
21 |
| - /// Configuration |
22 |
| - pub config: BuilderConfig, |
23 |
| - inner: Arc<RwLock<AuthenticatorInner>>, |
24 |
| -} |
| 14 | +/// A shared token that can be read and written to by multiple threads. |
| 15 | +#[derive(Debug, Clone, Default)] |
| 16 | +pub struct SharedToken(Arc<Mutex<Option<Token>>>); |
25 | 17 |
|
26 |
| -/// Inner state of the Authenticator. |
27 |
| -/// Contains the token that is being used for authentication. |
28 |
| -#[derive(Debug)] |
29 |
| -pub struct AuthenticatorInner { |
30 |
| - /// The token |
31 |
| - pub token: Option<Token>, |
32 |
| -} |
| 18 | +impl SharedToken { |
| 19 | + /// Read the token from the shared token. |
| 20 | + pub fn read(&self) -> Option<Token> { |
| 21 | + self.0.lock().unwrap().clone() |
| 22 | + } |
33 | 23 |
|
34 |
| -impl Default for AuthenticatorInner { |
35 |
| - fn default() -> Self { |
36 |
| - Self::new() |
| 24 | + /// Write a new token to the shared token. |
| 25 | + pub fn write(&self, token: Token) { |
| 26 | + let mut lock = self.0.lock().unwrap(); |
| 27 | + *lock = Some(token); |
37 | 28 | }
|
38 |
| -} |
39 | 29 |
|
40 |
| -impl AuthenticatorInner { |
41 |
| - /// Creates a new AuthenticatorInner with no token set. |
42 |
| - pub const fn new() -> Self { |
43 |
| - Self { token: None } |
| 30 | + /// Check if the token is authenticated. |
| 31 | + pub fn is_authenticated(&self) -> bool { |
| 32 | + self.0.lock().unwrap().is_some() |
44 | 33 | }
|
45 | 34 | }
|
46 | 35 |
|
| 36 | +/// A self-refreshing, periodically fetching authenticator for the block |
| 37 | +/// builder. This task periodically fetches a new token, and stores it in a |
| 38 | +/// [`SharedToken`]. |
| 39 | +#[derive(Debug)] |
| 40 | +pub struct Authenticator { |
| 41 | + /// Configuration |
| 42 | + pub config: BuilderConfig, |
| 43 | + client: BasicClient, |
| 44 | + token: SharedToken, |
| 45 | +} |
| 46 | + |
47 | 47 | impl Authenticator {
|
48 | 48 | /// Creates a new Authenticator from the provided builder config.
|
49 |
| - pub fn new(config: &BuilderConfig) -> Self { |
50 |
| - Self { config: config.clone(), inner: Arc::new(RwLock::new(AuthenticatorInner::new())) } |
| 49 | + pub fn new(config: &BuilderConfig) -> eyre::Result<Self> { |
| 50 | + let client = BasicClient::new( |
| 51 | + ClientId::new(config.oauth_client_id.clone()), |
| 52 | + Some(ClientSecret::new(config.oauth_client_secret.clone())), |
| 53 | + AuthUrl::new(config.oauth_authenticate_url.clone())?, |
| 54 | + Some(TokenUrl::new(config.oauth_token_url.clone())?), |
| 55 | + ); |
| 56 | + |
| 57 | + Ok(Self { config: config.clone(), client, token: Default::default() }) |
51 | 58 | }
|
52 | 59 |
|
53 | 60 | /// Requests a new authentication token and, if successful, sets it to as the token
|
54 | 61 | pub async fn authenticate(&self) -> eyre::Result<()> {
|
55 | 62 | let token = self.fetch_oauth_token().await?;
|
56 |
| - self.set_token(token).await; |
| 63 | + self.set_token(token); |
57 | 64 | Ok(())
|
58 | 65 | }
|
59 | 66 |
|
60 | 67 | /// Returns true if there is Some token set
|
61 |
| - pub async fn is_authenticated(&self) -> bool { |
62 |
| - let lock = self.inner.read().await; |
63 |
| - |
64 |
| - lock.token.is_some() |
| 68 | + pub fn is_authenticated(&self) -> bool { |
| 69 | + self.token.is_authenticated() |
65 | 70 | }
|
66 | 71 |
|
67 | 72 | /// Sets the Authenticator's token to the provided value
|
68 |
| - pub async fn set_token( |
69 |
| - &self, |
70 |
| - token: StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>, |
71 |
| - ) { |
72 |
| - let mut lock = self.inner.write().await; |
73 |
| - lock.token = Some(token); |
| 73 | + fn set_token(&self, token: StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>) { |
| 74 | + self.token.write(token); |
74 | 75 | }
|
75 | 76 |
|
76 | 77 | /// Returns the currently set token
|
77 |
| - pub async fn token(&self) -> Option<Token> { |
78 |
| - let lock = self.inner.read().await; |
79 |
| - lock.token.clone() |
| 78 | + pub fn token(&self) -> SharedToken { |
| 79 | + self.token.clone() |
80 | 80 | }
|
81 | 81 |
|
82 | 82 | /// Fetches an oauth token
|
83 |
| - pub async fn fetch_oauth_token( |
84 |
| - &self, |
85 |
| - ) -> eyre::Result<StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>> { |
86 |
| - let config = self.config.clone(); |
87 |
| - |
88 |
| - let client = BasicClient::new( |
89 |
| - ClientId::new(config.oauth_client_id.clone()), |
90 |
| - Some(ClientSecret::new(config.oauth_client_secret.clone())), |
91 |
| - AuthUrl::new(config.oauth_authenticate_url.clone())?, |
92 |
| - Some(TokenUrl::new(config.oauth_token_url.clone())?), |
93 |
| - ); |
94 |
| - |
| 83 | + async fn fetch_oauth_token(&self) -> eyre::Result<Token> { |
95 | 84 | let token_result =
|
96 |
| - client.exchange_client_credentials().request_async(async_http_client).await?; |
| 85 | + self.client.exchange_client_credentials().request_async(async_http_client).await?; |
97 | 86 |
|
98 | 87 | Ok(token_result)
|
99 | 88 | }
|
|
0 commit comments