Remove instances of cloning ApplicationSecret

ApplicationSecret is not a small struct. This removes the instances
where it's cloned in favor of passing a shared reference.
This commit is contained in:
Glenn Griffin
2019-11-08 13:40:34 -08:00
parent 696577aa01
commit 9542e3a9f1
7 changed files with 73 additions and 58 deletions

View File

@@ -216,7 +216,7 @@ where
let store = store.clone();
let rr = RefreshFlow::refresh_token(
client.clone(),
appsecret.clone(),
appsecret,
refresh_token.unwrap(),
)
.await?;
@@ -291,7 +291,7 @@ impl<
self.inner.api_key()
}
/// Returns the application secret of the inner flow.
fn application_secret(&self) -> ApplicationSecret {
fn application_secret(&self) -> &ApplicationSecret {
self.inner.application_secret()
}

View File

@@ -133,7 +133,7 @@ pub trait FlowDelegate: Clone + Send + Sync {
}
/// Configure a custom redirect uri if needed.
fn redirect_uri(&self) -> Option<String> {
fn redirect_uri(&self) -> Option<&str> {
None
}
/// The server has returned a `user_code` which must be shown to the user,

View File

@@ -114,8 +114,8 @@ where
fn api_key(&self) -> Option<String> {
None
}
fn application_secret(&self) -> ApplicationSecret {
self.application_secret.clone()
fn application_secret(&self) -> &ApplicationSecret {
&self.application_secret
}
}
@@ -135,12 +135,12 @@ where
where
T: AsRef<str>,
{
let application_secret = self.application_secret.clone();
let application_secret = &self.application_secret;
let client = self.client.clone();
let wait = self.wait;
let fd = self.fd.clone();
let (pollinf, device_code) = Self::request_code(
application_secret.clone(),
application_secret,
client.clone(),
self.device_code_url.clone(),
scopes,
@@ -153,7 +153,7 @@ where
let pollinf = pollinf.clone();
tokio::timer::delay_for(pollinf.interval).await;
let r = Self::poll_token(
application_secret.clone(),
application_secret,
client.clone(),
device_code.clone(),
pollinf.clone(),
@@ -194,7 +194,7 @@ where
/// # Examples
/// See test-cases in source code for a more complete example.
async fn request_code<T>(
application_secret: ApplicationSecret,
application_secret: &ApplicationSecret,
client: hyper::Client<C>,
device_code_url: String,
scopes: &[T],
@@ -206,8 +206,8 @@ where
// https://github.com/servo/rust-url/issues/81
let req = form_urlencoded::Serializer::new(String::new())
.extend_pairs(&[
("client_id", application_secret.client_id.clone()),
("scope", crate::helper::join(scopes, " ")),
("client_id", application_secret.client_id.as_str()),
("scope", crate::helper::join(scopes, " ").as_str()),
])
.finish();
@@ -276,7 +276,7 @@ where
/// # Examples
/// See test-cases in source code for a more complete example.
async fn poll_token<'a>(
application_secret: ApplicationSecret,
application_secret: &ApplicationSecret,
client: hyper::Client<C>,
device_code: String,
pi: PollInformation,
@@ -290,8 +290,8 @@ where
// We should be ready for a new request
let req = form_urlencoded::Serializer::new(String::new())
.extend_pairs(&[
("client_id", &application_secret.client_id[..]),
("client_secret", &application_secret.client_secret),
("client_id", application_secret.client_id.as_str()),
("client_secret", application_secret.client_secret.as_str()),
("code", &device_code),
("grant_type", "http://oauth.net/grant_type/device/1.0"),
])

View File

@@ -24,24 +24,17 @@ const OOB_REDIRECT_URI: &'static str = "urn:ietf:wg:oauth:2.0:oob";
/// Assembles a URL to request an authorization token (with user interaction).
/// Note that the redirect_uri here has to be either None or some variation of
/// http://localhost:{port}, or the authorization won't work (error "redirect_uri_mismatch")
fn build_authentication_request_url<'a, T, I>(
fn build_authentication_request_url<T>(
auth_uri: &str,
client_id: &str,
scopes: I,
redirect_uri: Option<String>,
scopes: &[T],
redirect_uri: Option<&str>,
) -> String
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
T: AsRef<str>,
{
let mut url = String::new();
let mut scopes_string = scopes.into_iter().fold(String::new(), |mut acc, sc| {
acc.push_str(sc.as_ref());
acc.push_str(" ");
acc
});
// Remove last space
scopes_string.pop();
let scopes_string = crate::helper::join(scopes, " ");
url.push_str(auth_uri);
vec![
@@ -49,7 +42,7 @@ where
format!("&access_type=offline"),
format!(
"&redirect_uri={}",
redirect_uri.unwrap_or(OOB_REDIRECT_URI.to_string())
redirect_uri.unwrap_or(OOB_REDIRECT_URI)
),
format!("&response_type=code"),
format!("&client_id={}", client_id),
@@ -78,8 +71,8 @@ where
fn api_key(&self) -> Option<String> {
None
}
fn application_secret(&self) -> ApplicationSecret {
self.appsecret.clone()
fn application_secret(&self) -> &ApplicationSecret {
&self.appsecret
}
}
@@ -232,6 +225,7 @@ where
where
T: AsRef<str>,
{
use std::borrow::Cow;
let auth_delegate = &self.fd;
let appsecret = &self.appsecret;
let server = InstalledFlowServer::run(desired_port)?;
@@ -240,13 +234,15 @@ where
// Present url to user.
// The redirect URI must be this very localhost URL, otherwise authorization is refused
// by certain providers.
let redirect_uri: Cow<str> = match auth_delegate.redirect_uri() {
Some(uri) => uri.into(),
None => format!("http://localhost:{}", bound_port).into(),
};
let url = build_authentication_request_url(
&appsecret.auth_uri,
&appsecret.client_id,
scopes,
auth_delegate
.redirect_uri()
.or_else(|| Some(format!("http://localhost:{}", bound_port))),
Some(redirect_uri.as_ref()),
);
let _ = auth_delegate
.present_user_url(&url, false /* need code */)
@@ -262,8 +258,8 @@ where
port: Option<u16>,
) -> Result<Token, RequestError> {
let appsec = &self.appsecret;
let redirect_uri = &self.fd.redirect_uri();
let request = Self::request_token(appsec.clone(), authcode, redirect_uri.clone(), port);
let redirect_uri = self.fd.redirect_uri();
let request = Self::request_token(appsec, authcode, redirect_uri, port);
let resp = self
.client
.request(request)
@@ -310,27 +306,29 @@ where
/// Sends the authorization code to the provider in order to obtain access and refresh tokens.
fn request_token<'a>(
appsecret: ApplicationSecret,
appsecret: &ApplicationSecret,
authcode: String,
custom_redirect_uri: Option<String>,
custom_redirect_uri: Option<&str>,
port: Option<u16>,
) -> hyper::Request<hyper::Body> {
let redirect_uri = custom_redirect_uri.unwrap_or_else(|| match port {
None => OOB_REDIRECT_URI.to_string(),
Some(port) => format!("http://localhost:{}", port),
});
use std::borrow::Cow;
let redirect_uri: Cow<str> = match (custom_redirect_uri, port) {
(Some(uri), _) => uri.into(),
(None, Some(port)) => format!("http://localhost:{}", port).into(),
(None, None) => OOB_REDIRECT_URI.into(),
};
let body = form_urlencoded::Serializer::new(String::new())
.extend_pairs(vec![
("code".to_string(), authcode.to_string()),
("client_id".to_string(), appsecret.client_id.clone()),
("client_secret".to_string(), appsecret.client_secret.clone()),
("redirect_uri".to_string(), redirect_uri),
("grant_type".to_string(), "authorization_code".to_string()),
("code", authcode.as_str()),
("client_id", appsecret.client_id.as_str()),
("client_secret", appsecret.client_secret.as_str()),
("redirect_uri", redirect_uri.as_ref()),
("grant_type", "authorization_code"),
])
.finish();
let request = hyper::Request::post(appsecret.token_uri)
let request = hyper::Request::post(&appsecret.token_uri)
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(hyper::Body::from(body))
.unwrap(); // TODO: error check
@@ -657,7 +655,7 @@ mod tests {
"https://accounts.google.com/o/oauth2/auth",
"812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5am\
rf.apps.googleusercontent.com",
vec![&"email".to_string(), &"profile".to_string()],
&["email", "profile"],
None
)
);

View File

@@ -32,20 +32,20 @@ impl RefreshFlow {
/// Please see the crate landing page for an example.
pub async fn refresh_token<C: 'static + hyper::client::connect::Connect>(
client: hyper::Client<C>,
client_secret: ApplicationSecret,
client_secret: &ApplicationSecret,
refresh_token: String,
) -> Result<RefreshResult, RequestError> {
// TODO: Does this function ever return RequestError? Maybe have it just return RefreshResult.
let req = form_urlencoded::Serializer::new(String::new())
.extend_pairs(&[
("client_id", client_secret.client_id.clone()),
("client_secret", client_secret.client_secret.clone()),
("refresh_token", refresh_token.to_string()),
("grant_type", "refresh_token".to_string()),
("client_id", client_secret.client_id.as_str()),
("client_secret", client_secret.client_secret.as_str()),
("refresh_token", refresh_token.as_str()),
("grant_type", "refresh_token"),
])
.finish();
let request = hyper::Request::post(client_secret.token_uri.clone())
let request = hyper::Request::post(&client_secret.token_uri)
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(hyper::Body::from(req))
.unwrap(); // TODO: error handling
@@ -130,7 +130,7 @@ mod tests {
let fut = async {
let rr = RefreshFlow::refresh_token(
client.clone(),
app_secret.clone(),
&app_secret,
refresh_token.clone(),
)
.await
@@ -158,7 +158,7 @@ mod tests {
.create();
let fut = async {
let rr = RefreshFlow::refresh_token(client, app_secret, refresh_token)
let rr = RefreshFlow::refresh_token(client, &app_secret, refresh_token)
.await
.unwrap();
match rr {

View File

@@ -382,8 +382,9 @@ where
/// Returns an empty ApplicationSecret as tokens for service accounts don't need to be
/// refreshed (they are simply reissued).
fn application_secret(&self) -> ApplicationSecret {
Default::default()
fn application_secret(&self) -> &ApplicationSecret {
static APP_SECRET: ApplicationSecret = ApplicationSecret::empty();
&APP_SECRET
}
fn api_key(&self) -> Option<String> {

View File

@@ -247,7 +247,7 @@ pub trait GetToken: Send + Sync {
/// Return an application secret with at least token_uri, client_secret, and client_id filled
/// in. This is used for refreshing tokens without interaction from the flow.
fn application_secret(&self) -> ApplicationSecret;
fn application_secret(&self) -> &ApplicationSecret;
}
/// Represents a token as returned by OAuth2 servers.
@@ -342,6 +342,22 @@ pub struct ApplicationSecret {
pub client_x509_cert_url: Option<String>,
}
impl ApplicationSecret {
pub const fn empty() -> Self {
ApplicationSecret{
client_id: String::new(),
client_secret: String::new(),
token_uri: String::new(),
auth_uri: String::new(),
redirect_uris: Vec::new(),
project_id: None,
client_email: None,
auth_provider_x509_cert_url: None,
client_x509_cert_url: None,
}
}
}
/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(Deserialize, Serialize, Default)]