aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock63
-rw-r--r--Cargo.toml1
-rw-r--r--src/client.rs18
-rw-r--r--src/cmd/stream.rs12
-rw-r--r--src/cmd/watch.rs13
-rw-r--r--src/dirs.rs40
-rw-r--r--src/error.rs6
-rw-r--r--src/main.rs2
-rw-r--r--src/oauth.rs68
-rw-r--r--src/oauth/recurse_center.rs34
-rw-r--r--src/prelude.rs1
-rw-r--r--src/server.rs126
12 files changed, 331 insertions, 53 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1288de6..56a6bd5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,6 +96,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "blake2b_simd"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "block-buffer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,6 +176,11 @@ dependencies = [
]
[[package]]
+name = "constant_time_eq"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "cookie"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -373,6 +388,26 @@ dependencies = [
]
[[package]]
+name = "directories"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "doc-comment"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1152,6 +1187,17 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "redox_users"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "regex"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1209,6 +1255,16 @@ dependencies = [
]
[[package]]
+name = "rust-argon2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1438,6 +1494,7 @@ dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossterm 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1933,6 +1990,7 @@ dependencies = [
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
+"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
@@ -1942,6 +2000,7 @@ dependencies = [
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
"checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
"checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c"
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
@@ -1962,6 +2021,8 @@ dependencies = [
"checksum curl 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)" = "06aa71e9208a54def20792d877bc663d6aae0732b9852e612c4a933177c31283"
"checksum curl-sys 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)" = "f71cd2dbddb49c744c1c9e0b96106f50a634e8759ec51bcd5399a578700a3ab3"
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
+"checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
+"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
@@ -2051,10 +2112,12 @@ dependencies = [
"checksum ratelimit_meter 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a37d4f95369ef809d01448bdf8d82ef974e3b21f3698d87015a575bb26f27724"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650"
+"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
diff --git a/Cargo.toml b/Cargo.toml
index ed43526..be024d7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
bytes = "0.4"
clap = "2"
crossterm = "0.11"
+directories = "2"
env_logger = "0.7"
futures = "0.1"
lazy_static = "1"
diff --git a/src/client.rs b/src/client.rs
index e351eec..59ed96d 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -280,8 +280,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
msg.log("recv");
match msg {
- // XXX store the id and use it on future requests
- crate::protocol::Message::OauthRequest { url, id: _id } => {
+ crate::protocol::Message::OauthRequest { url, id } => {
let mut state = None;
let parsed_url = url::Url::parse(&url).unwrap();
for (k, v) in parsed_url.query_pairs() {
@@ -294,6 +293,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
crate::component_future::Poll::DidWork,
Some(self.wait_for_oauth_response(
state.map(|s| s.to_string()),
+ &id,
)?),
))
}
@@ -322,6 +322,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
fn wait_for_oauth_response(
&self,
state: Option<String>,
+ id: &str,
) -> Result<
Box<
dyn futures::future::Future<
@@ -336,6 +337,8 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
).unwrap();
}
+ let auth_name = self.auth.name().to_string();
+ let id = id.to_string();
let addr = OAUTH_LISTEN_ADDRESS
.parse()
.context(crate::error::ParseAddr)?;
@@ -390,6 +393,17 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
lines.into_inner().into_inner(),
))
})
+ .and_then(move |(msg, sock)| {
+ let id_file = crate::dirs::Dirs::new()
+ .data_file(&format!("client-oauth-{}", auth_name));
+ tokio::fs::File::create(id_file)
+ .context(crate::error::CreateFile)
+ .and_then(|file| {
+ tokio::io::write_all(file, id)
+ .context(crate::error::WriteFile)
+ })
+ .map(|_| (msg, sock))
+ })
.and_then(|(msg, sock)| {
let response = r"HTTP/1.1 200 OK
diff --git a/src/cmd/stream.rs b/src/cmd/stream.rs
index 66ed68b..c1e1efa 100644
--- a/src/cmd/stream.rs
+++ b/src/cmd/stream.rs
@@ -1,4 +1,5 @@
use crate::prelude::*;
+use std::io::Read as _;
use tokio::io::AsyncWrite as _;
pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
@@ -30,7 +31,16 @@ pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
pub fn run<'a>(matches: &clap::ArgMatches<'a>) -> super::Result<()> {
let auth = if matches.is_present("login-recurse-center") {
- crate::protocol::Auth::RecurseCenter { id: None }
+ let auth = crate::protocol::Auth::RecurseCenter { id: None };
+ let id_file = crate::dirs::Dirs::new()
+ .data_file(&format!("client-oauth-{}", auth.name()));
+ let id = std::fs::File::open(id_file).ok().and_then(|mut file| {
+ let mut id = vec![];
+ file.read_to_end(&mut id).ok().map(|_| {
+ std::string::String::from_utf8_lossy(&id).to_string()
+ })
+ });
+ crate::protocol::Auth::RecurseCenter { id }
} else {
let username = matches
.value_of("login-plain")
diff --git a/src/cmd/watch.rs b/src/cmd/watch.rs
index a4e5efa..d400cfc 100644
--- a/src/cmd/watch.rs
+++ b/src/cmd/watch.rs
@@ -1,5 +1,5 @@
use crate::prelude::*;
-use std::io::Write as _;
+use std::io::{Read as _, Write as _};
pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
app.about("Watch teleterm streams")
@@ -23,7 +23,16 @@ pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
pub fn run<'a>(matches: &clap::ArgMatches<'a>) -> super::Result<()> {
let auth = if matches.is_present("login-recurse-center") {
- crate::protocol::Auth::RecurseCenter { id: None }
+ let auth = crate::protocol::Auth::RecurseCenter { id: None };
+ let id_file = crate::dirs::Dirs::new()
+ .data_file(&format!("client-oauth-{}", auth.name()));
+ let id = std::fs::File::open(id_file).ok().and_then(|mut file| {
+ let mut id = vec![];
+ file.read_to_end(&mut id).ok().map(|_| {
+ std::string::String::from_utf8_lossy(&id).to_string()
+ })
+ });
+ crate::protocol::Auth::RecurseCenter { id }
} else {
let username = matches
.value_of("login-plain")
diff --git a/src/dirs.rs b/src/dirs.rs
new file mode 100644
index 0000000..12a2f15
--- /dev/null
+++ b/src/dirs.rs
@@ -0,0 +1,40 @@
+use crate::prelude::*;
+
+pub struct Dirs {
+ project_dirs: directories::ProjectDirs,
+}
+
+impl Dirs {
+ pub fn new() -> Self {
+ // TODO: fall back to /var, /etc, etc if we're running without $HOME
+ // set
+ Self {
+ project_dirs: directories::ProjectDirs::from("", "", "teleterm")
+ .expect("failed to find valid home directory"),
+ }
+ }
+
+ pub fn create_all(&self) -> Result<()> {
+ std::fs::create_dir_all(self.cache_dir())
+ .context(crate::error::CreateDir)?;
+ std::fs::create_dir_all(self.data_dir())
+ .context(crate::error::CreateDir)?;
+ Ok(())
+ }
+
+ fn cache_dir(&self) -> &std::path::Path {
+ self.project_dirs.cache_dir()
+ }
+
+ pub fn cache_file(&self, name: &str) -> std::path::PathBuf {
+ self.cache_dir().join(name)
+ }
+
+ fn data_dir(&self) -> &std::path::Path {
+ self.project_dirs.data_dir()
+ }
+
+ pub fn data_file(&self, name: &str) -> std::path::PathBuf {
+ self.data_dir().join(name)
+ }
+}
diff --git a/src/error.rs b/src/error.rs
index 337c9f6..e03ffdf 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,6 +22,12 @@ pub enum Error {
#[snafu(display("failed to create tls connector: {}", source))]
CreateConnector { source: native_tls::Error },
+ #[snafu(display("failed to create directory: {}", source))]
+ CreateDir { source: std::io::Error },
+
+ #[snafu(display("failed to create file: {}", source))]
+ CreateFile { source: tokio::io::Error },
+
#[snafu(display("eof"))]
EOF,
diff --git a/src/main.rs b/src/main.rs
index 89124d9..edba700 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,7 @@ mod async_stdin;
mod client;
mod cmd;
mod component_future;
+mod dirs;
mod error;
mod key_reader;
mod oauth;
@@ -25,6 +26,7 @@ mod ttyrec;
mod util;
fn main() {
+ dirs::Dirs::new().create_all().unwrap();
env_logger::from_env(
env_logger::Env::default().default_filter_or("info"),
)
diff --git a/src/oauth.rs b/src/oauth.rs
index 8d1944a..e23c44e 100644
--- a/src/oauth.rs
+++ b/src/oauth.rs
@@ -1,9 +1,16 @@
use crate::prelude::*;
+use oauth2::TokenResponse as _;
pub mod recurse_center;
pub trait Oauth {
fn client(&self) -> &oauth2::basic::BasicClient;
+ fn user_id(&self) -> &str;
+ fn name(&self) -> &str;
+ fn token_cache_file(&self) -> std::path::PathBuf {
+ let name = format!("server-oauth-{}-{}", self.name(), self.user_id());
+ crate::dirs::Dirs::new().cache_file(&name)
+ }
fn generate_authorize_url(&self) -> String {
let (auth_url, _) = self
@@ -13,15 +20,12 @@ pub trait Oauth {
auth_url.to_string()
}
- fn get_token(
+ fn get_access_token_from_auth_code(
&self,
code: &str,
- ) -> Box<
- dyn futures::future::Future<
- Item = oauth2::basic::BasicTokenResponse,
- Error = Error,
- > + Send,
- > {
+ ) -> Box<dyn futures::future::Future<Item = String, Error = Error> + Send>
+ {
+ let token_cache_file = self.token_cache_file();
let fut = self
.client()
.exchange_code(oauth2::AuthorizationCode::new(code.to_string()))
@@ -29,16 +33,62 @@ pub trait Oauth {
.map_err(|e| {
let msg = stringify_oauth2_http_error(&e);
Error::ExchangeCode { msg }
+ })
+ .and_then(move |token| {
+ cache_refresh_token(token_cache_file, &token)
+ .map(move |_| token.access_token().secret().to_string())
});
Box::new(fut)
}
- fn get_username(
+ fn get_access_token_from_refresh_token(
&self,
- code: &str,
+ token: &str,
+ ) -> Box<dyn futures::future::Future<Item = String, Error = Error> + Send>
+ {
+ let token_cache_file = self.token_cache_file();
+ let fut = self
+ .client()
+ .exchange_refresh_token(&oauth2::RefreshToken::new(
+ token.to_string(),
+ ))
+ .request_async(oauth2::reqwest::async_http_client)
+ .map_err(|e| {
+ let msg = stringify_oauth2_http_error(&e);
+ Error::ExchangeCode { msg }
+ })
+ .and_then(move |token| {
+ cache_refresh_token(token_cache_file, &token)
+ .map(move |_| token.access_token().secret().to_string())
+ });
+ Box::new(fut)
+ }
+
+ fn get_username_from_access_token(
+ self: Box<Self>,
+ token: &str,
) -> Box<dyn futures::future::Future<Item = String, Error = Error> + Send>;
}
+fn cache_refresh_token(
+ token_cache_file: std::path::PathBuf,
+ token: &oauth2::basic::BasicTokenResponse,
+) -> Box<dyn futures::future::Future<Item = (), Error = Error> + Send> {
+ let token_data = format!(
+ "{}\n{}\n",
+ token.refresh_token().unwrap().secret(),
+ token.access_token().secret(),
+ );
+ let fut = tokio::fs::File::create(token_cache_file)
+ .context(crate::error::CreateFile)
+ .and_then(|file| {
+ tokio::io::write_all(file, token_data)
+ .context(crate::error::WriteFile)
+ })
+ .map(|_| ());
+ Box::new(fut)
+}
+
pub struct Config {
client_id: String,
client_secret: String,
diff --git a/src/oauth/recurse_center.rs b/src/oauth/recurse_center.rs
index 26aa306..1bd6de7 100644
--- a/src/oauth/recurse_center.rs
+++ b/src/oauth/recurse_center.rs
@@ -1,14 +1,15 @@
use crate::prelude::*;
-use oauth2::TokenResponse as _;
pub struct Oauth {
client: oauth2::basic::BasicClient,
+ user_id: String,
}
impl Oauth {
- pub fn new(config: super::Config) -> Self {
+ pub fn new(config: super::Config, user_id: &str) -> Self {
Self {
client: config.into_basic_client(),
+ user_id: user_id.to_string(),
}
}
}
@@ -18,21 +19,24 @@ impl super::Oauth for Oauth {
&self.client
}
- fn get_username(
- &self,
- code: &str,
+ fn user_id(&self) -> &str {
+ &self.user_id
+ }
+
+ fn name(&self) -> &str {
+ "recurse_center"
+ }
+
+ fn get_username_from_access_token(
+ self: Box<Self>,
+ token: &str,
) -> Box<dyn futures::future::Future<Item = String, Error = Error> + Send>
{
- let fut = self
- .get_token(code)
- .and_then(|token| {
- let access_token = token.access_token();
- reqwest::r#async::Client::new()
- .get("https://www.recurse.com/api/v1/profiles/me")
- .bearer_auth(access_token.secret())
- .send()
- .context(crate::error::GetProfile)
- })
+ let fut = reqwest::r#async::Client::new()
+ .get("https://www.recurse.com/api/v1/profiles/me")
+ .bearer_auth(token)
+ .send()
+ .context(crate::error::GetProfile)
.and_then(|mut res| res.json().context(crate::error::ParseJson))
.map(|user: User| user.name());
Box::new(fut)
diff --git a/src/prelude.rs b/src/prelude.rs
index 05ebbe3..1bd29b7 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -6,3 +6,4 @@ pub use snafu::futures01::FutureExt as _;
pub use snafu::{OptionExt as _, ResultExt as _};
pub use crate::error::{Error, Result};
+pub use crate::oauth::Oauth as _;
diff --git a/src/server.rs b/src/server.rs
index 5b82f3c..90728dd 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -147,6 +147,25 @@ impl ConnectionState {
}
}
+ fn login_oauth(
+ &mut self,
+ term_type: &str,
+ size: &crate::term::Size,
+ username: &str,
+ ) {
+ if let Self::Accepted = self {
+ *self = Self::LoggedIn {
+ username: username.to_string(),
+ term_info: TerminalInfo {
+ term: term_type.to_string(),
+ size: size.clone(),
+ },
+ };
+ } else {
+ unreachable!()
+ }
+ }
+
fn login_oauth_start(
&mut self,
term_type: &str,
@@ -340,7 +359,16 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
auth: &crate::protocol::Auth,
term_type: &str,
size: crate::term::Size,
- ) -> Result<()> {
+ ) -> Result<
+ Option<
+ Box<
+ dyn futures::future::Future<
+ Item = (ConnectionState, crate::protocol::Message),
+ Error = Error,
+ > + Send,
+ >,
+ >,
+ > {
if size.rows >= 1000 || size.cols >= 1000 {
return Err(Error::TermTooBig { size });
}
@@ -359,7 +387,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
));
}
oauth if oauth.is_oauth() => {
- let (id, client) = match oauth {
+ let (refresh, client) = match oauth {
crate::protocol::Auth::RecurseCenter { id } => {
// XXX this needs some kind of real configuration
// system
@@ -376,7 +404,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
url::Url::parse(&redirect_url).unwrap();
(
- id,
+ id.is_some(),
Box::new(
crate::oauth::recurse_center::Oauth::new(
crate::oauth::recurse_center::config(
@@ -384,6 +412,9 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
&client_secret,
redirect_url,
),
+ &id.clone().unwrap_or_else(|| {
+ format!("{}", uuid::Uuid::new_v4())
+ }),
),
),
)
@@ -391,29 +422,64 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
_ => unreachable!(),
};
+ conn.oauth_client = Some(client);
+ let client = conn.oauth_client.as_ref().unwrap();
+
log::info!(
"{}: login(oauth({}), {:?})",
conn.id,
auth.name(),
- id
+ client.user_id()
);
- conn.oauth_client = Some(client);
- if let Some(_id) = id {
- // refresh
- unimplemented!()
+ if refresh {
+ let term_type = term_type.to_string();
+ let client = conn.oauth_client.take().unwrap();
+ let mut new_state = conn.state.clone();
+ let fut =
+ tokio::fs::File::open(client.token_cache_file())
+ .context(crate::error::OpenFile)
+ .and_then(|file| {
+ tokio::io::lines(std::io::BufReader::new(
+ file,
+ ))
+ .into_future()
+ .map_err(|(e, _)| e)
+ .context(crate::error::ReadFile)
+ })
+ .and_then(|(refresh_token, _)| {
+ // XXX unwrap here isn't super safe
+ let refresh_token = refresh_token.unwrap();
+ client
+ .get_access_token_from_refresh_token(
+ refresh_token.trim(),
+ )
+ .and_then(|access_token| {
+ client.get_username_from_access_token(
+ &access_token,
+ )
+ })
+ })
+ .map(move |username| {
+ new_state.login_oauth(
+ &term_type, &size, &username,
+ );
+ (
+ new_state,
+ crate::protocol::Message::logged_in(
+ &username,
+ ),
+ )
+ });
+ return Ok(Some(Box::new(fut)));
} else {
- let id = format!("{}", uuid::Uuid::new_v4());
-
conn.state.login_oauth_start(term_type, &size);
+ let authorize_url = client.generate_authorize_url();
+ let user_id = client.user_id().to_string();
conn.send_message(
crate::protocol::Message::oauth_request(
- &conn
- .oauth_client
- .as_ref()
- .unwrap()
- .generate_authorize_url(),
- &id,
+ &authorize_url,
+ &user_id,
),
);
}
@@ -421,7 +487,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
_ => unreachable!(),
}
- Ok(())
+ Ok(None)
}
fn handle_message_start_streaming(
@@ -532,17 +598,20 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
>,
>,
> {
- let client = conn.oauth_client.as_ref().ok_or_else(|| {
+ let client = conn.oauth_client.take().ok_or_else(|| {
Error::UnexpectedMessage {
message: crate::protocol::Message::oauth_response(code),
}
})?;
let mut new_state = conn.state.clone();
- let fut = client.get_username(code).map(|username| {
- new_state.login_oauth_finish(&username);
- (new_state, crate::protocol::Message::logged_in(&username))
- });
+ let fut = client
+ .get_access_token_from_auth_code(code)
+ .and_then(|token| client.get_username_from_access_token(&token))
+ .map(|username| {
+ new_state.login_oauth_finish(&username);
+ (new_state, crate::protocol::Message::logged_in(&username))
+ });
Ok(Some(Box::new(fut)))
}
@@ -551,7 +620,16 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
&mut self,
conn: &mut Connection<S>,
message: crate::protocol::Message,
- ) -> Result<()> {
+ ) -> Result<
+ Option<
+ Box<
+ dyn futures::future::Future<
+ Item = (ConnectionState, crate::protocol::Message),
+ Error = Error,
+ > + Send,
+ >,
+ >,
+ > {
match message {
crate::protocol::Message::Login {
auth,
@@ -691,7 +769,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
match conn.state {
ConnectionState::Accepted { .. } => {
- self.handle_accepted_message(conn, message).map(|_| None)
+ self.handle_accepted_message(conn, message)
}
ConnectionState::LoggingIn { .. } => {
self.handle_logging_in_message(conn, message)