Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(services/gdrive): credential manage #2914

Merged
merged 3 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/services/dropbox/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub struct DropboxBuilder {

impl Debug for DropboxBuilder {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Builder").finish()
f.debug_struct("Builder").field("root", &self.root).finish()
}
}

Expand Down
10 changes: 6 additions & 4 deletions core/src/services/dropbox/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ use crate::types::ErrorKind;
use crate::types::Result;

pub struct DropboxCore {
pub signer: Arc<Mutex<DropboxSigner>>,
pub client: HttpClient,
pub root: String,

pub client: HttpClient,

pub signer: Arc<Mutex<DropboxSigner>>,
}

impl Debug for DropboxCore {
Expand Down Expand Up @@ -360,11 +362,11 @@ pub struct DropboxSigner {
impl Default for DropboxSigner {
fn default() -> Self {
DropboxSigner {
refresh_token: "".to_string(),
refresh_token: String::new(),
client_id: String::new(),
client_secret: String::new(),

access_token: "".to_string(),
access_token: String::new(),
expires_in: DateTime::<Utc>::MIN_UTC,
}
}
Expand Down
15 changes: 1 addition & 14 deletions core/src/services/gdrive/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,7 @@ use crate::*;

#[derive(Clone, Debug)]
pub struct GdriveBackend {
core: Arc<GdriveCore>,
}

impl GdriveBackend {
pub(crate) fn new(root: String, access_token: String, http_client: HttpClient) -> Self {
GdriveBackend {
core: Arc::new(GdriveCore {
root,
access_token,
client: http_client,
path_cache: Arc::default(),
}),
}
}
pub core: Arc<GdriveCore>,
}

#[async_trait]
Expand Down
118 changes: 109 additions & 9 deletions core/src/services/gdrive/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::sync::Arc;

use chrono::DateTime;
use chrono::Utc;
use log::debug;
use tokio::sync::Mutex;

use super::backend::GdriveBackend;
use crate::raw::normalize_root;
use crate::raw::HttpClient;
use crate::services::gdrive::core::GdriveCore;
use crate::services::gdrive::core::GdriveSigner;
use crate::Scheme;
use crate::*;

Expand Down Expand Up @@ -79,8 +85,14 @@ use crate::*;
/// ```
#[derive(Default)]
pub struct GdriveBuilder {
access_token: Option<String>,
root: Option<String>,

access_token: Option<String>,

refresh_token: Option<String>,
client_id: Option<String>,
client_secret: Option<String>,

http_client: Option<HttpClient>,
}

Expand All @@ -91,15 +103,50 @@ impl Debug for GdriveBuilder {
}

impl GdriveBuilder {
/// default: no access token, which leads to failure
/// Set root path of GoogleDrive folder.
pub fn root(&mut self, root: &str) -> &mut Self {
self.root = Some(root.to_string());
self
}

/// Access token is used for temporary access to the GoogleDrive API.
///
/// You can get the access token from [GoogleDrive App Console](https://console.cloud.google.com/apis/credentials)
/// or [GoogleDrive OAuth2 Playground](https://developers.google.com/oauthplayground/)
///
/// # Note
///
/// - An access token is valid for 1 hour.
/// - If you want to use the access token for a long time,
/// you can use the refresh token to get a new access token.
pub fn access_token(&mut self, access_token: &str) -> &mut Self {
self.access_token = Some(access_token.to_string());
self
}

/// Set root path of GoogleDrive folder.
pub fn root(&mut self, root: &str) -> &mut Self {
self.root = Some(root.to_string());
/// Refresh token is used for long term access to the GoogleDrive API.
///
/// You can get the refresh token via OAuth 2.0 Flow of GoogleDrive API.
///
/// OpenDAL will use this refresh token to get a new access token when the old one is expired.
pub fn refresh_token(&mut self, refresh_token: &str) -> &mut Self {
self.refresh_token = Some(refresh_token.to_string());
self
}

/// Set the client id for GoogleDrive.
///
/// This is required for OAuth 2.0 Flow to refresh the access token.
pub fn client_id(&mut self, client_id: &str) -> &mut Self {
self.client_id = Some(client_id.to_string());
self
}

/// Set the client secret for GoogleDrive.
///
/// This is required for OAuth 2.0 Flow with refresh the access token.
pub fn client_secret(&mut self, client_secret: &str) -> &mut Self {
self.client_secret = Some(client_secret.to_string());
self
}

Expand All @@ -125,6 +172,9 @@ impl Builder for GdriveBuilder {

map.get("root").map(|v| builder.root(v));
map.get("access_token").map(|v| builder.access_token(v));
map.get("refresh_token").map(|v| builder.refresh_token(v));
map.get("client_id").map(|v| builder.client_id(v));
map.get("client_secret").map(|v| builder.client_secret(v));

builder
}
Expand All @@ -142,9 +192,59 @@ impl Builder for GdriveBuilder {
})?
};

match self.access_token.clone() {
Some(access_token) => Ok(GdriveBackend::new(root, access_token, client)),
None => Err(Error::new(ErrorKind::ConfigInvalid, "access_token not set")),
}
let signer = match (self.access_token.take(), self.refresh_token.take()) {
(Some(access_token), None) => GdriveSigner {
access_token,
// We will never expire user specified access token.
expires_in: DateTime::<Utc>::MAX_UTC,
..Default::default()
},
(None, Some(refresh_token)) => {
let client_id = self.client_id.take().ok_or_else(|| {
Error::new(
ErrorKind::ConfigInvalid,
"client_id must be set when refresh_token is set",
)
.with_context("service", Scheme::Gdrive)
})?;
let client_secret = self.client_secret.take().ok_or_else(|| {
Error::new(
ErrorKind::ConfigInvalid,
"client_secret must be set when refresh_token is set",
)
.with_context("service", Scheme::Gdrive)
})?;

GdriveSigner {
refresh_token,
client_id,
client_secret,
..Default::default()
}
}
(Some(_), Some(_)) => {
return Err(Error::new(
ErrorKind::ConfigInvalid,
"access_token and refresh_token cannot be set at the same time",
)
.with_context("service", Scheme::Gdrive))
}
(None, None) => {
return Err(Error::new(
ErrorKind::ConfigInvalid,
"access_token or refresh_token must be set",
)
.with_context("service", Scheme::Gdrive))
}
};

Ok(GdriveBackend {
core: Arc::new(GdriveCore {
root,
signer: Arc::new(Mutex::new(signer)),
client,
path_cache: Arc::default(),
}),
})
}
}