aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-11-26 04:20:43 -0500
committerJesse Luehrs <doy@tozt.net>2019-11-26 04:20:43 -0500
commitea8beb985247aac4345ecefc8ec551f52f5f1a24 (patch)
tree7288af40b291e03252cfca7bb4654b63181776ed
parentcc5a3f9056fe1b563dbb40d51b1621769150c08d (diff)
downloadteleterm-ea8beb985247aac4345ecefc8ec551f52f5f1a24.tar.gz
teleterm-ea8beb985247aac4345ecefc8ec551f52f5f1a24.zip
allow multiple oauth configurations using the same auth type
this should allow us to configure a separate oauth application for tt web than normal (since the redirect_url needs to be different)
-rw-r--r--README.md19
-rw-r--r--teleterm/src/cmd/server.rs15
-rw-r--r--teleterm/src/cmd/stream.rs1
-rw-r--r--teleterm/src/cmd/watch.rs1
-rw-r--r--teleterm/src/config.rs99
-rw-r--r--teleterm/src/error.rs10
-rw-r--r--teleterm/src/oauth.rs2
-rw-r--r--teleterm/src/oauth/recurse_center.rs8
-rw-r--r--teleterm/src/protocol.rs96
-rw-r--r--teleterm/src/server.rs39
-rw-r--r--teleterm/src/server/tls.rs5
11 files changed, 225 insertions, 70 deletions
diff --git a/README.md b/README.md
index c797841..4bdf079 100644
--- a/README.md
+++ b/README.md
@@ -155,16 +155,23 @@ create one for you automatically. The configuration has several sections:
* Same as `uid`, except sets the user's primary group.
* Default: unset
-#### `[oauth.<method>]` (used by `tt server`)
+#### `[oauth.<method>.<client>]` (used by `tt server`)
-`<method>` corresponds to an OAuth-using login method - for instance, a section
-would be named something like `[oauth.recurse_center]`. Note that OAuth login
-methods are required to use `http://localhost:44141` as their redirect URL.
+`<method>` corresponds to an OAuth-using login method. Currently only
+`recurse_center` is supported. `<client>` describes what types of clients will
+be using this configuration. Currently valid values for `<client>` are `cli`
+(for `tt stream` and `tt watch`) and `web` (for `tt web`). For example, a valid
+configuration section will look like `[oauth.recurse_center.cli]`. You will
+need to configure separate OAuth applications for `cli` and `web` since the
+`redirect_url` will need to be different in each case.
* `client_id`
- * OAuth client id.
+ * OAuth client id. Required.
* `client_secret`
- * OAuth client secret.
+ * OAuth client secret. Required.
+* `redirect_url`
+ * OAuth client redirect url. Required if `<client>` is `web`, and must be
+ the `/oauth` path at the externally reachable domain of your web server.
#### `[client]` (used by `tt stream` and `tt watch`)
diff --git a/teleterm/src/cmd/server.rs b/teleterm/src/cmd/server.rs
index 7eff187..25571dc 100644
--- a/teleterm/src/cmd/server.rs
+++ b/teleterm/src/cmd/server.rs
@@ -13,7 +13,10 @@ pub struct Config {
)]
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
}
@@ -76,7 +79,10 @@ fn create_server(
>,
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
uid: Option<users::uid_t>,
gid: Option<users::gid_t>,
@@ -106,7 +112,10 @@ fn create_server_tls(
>,
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
uid: Option<users::uid_t>,
gid: Option<users::gid_t>,
diff --git a/teleterm/src/cmd/stream.rs b/teleterm/src/cmd/stream.rs
index 83556de..92026f1 100644
--- a/teleterm/src/cmd/stream.rs
+++ b/teleterm/src/cmd/stream.rs
@@ -38,6 +38,7 @@ impl crate::config::Config for Config {
crate::protocol::AuthType::RecurseCenter => {
let id = crate::oauth::load_client_auth_id(self.client.auth);
crate::protocol::Auth::recurse_center(
+ crate::protocol::AuthClient::Cli,
id.as_ref().map(std::string::String::as_str),
)
}
diff --git a/teleterm/src/cmd/watch.rs b/teleterm/src/cmd/watch.rs
index f205002..dc80d47 100644
--- a/teleterm/src/cmd/watch.rs
+++ b/teleterm/src/cmd/watch.rs
@@ -36,6 +36,7 @@ impl crate::config::Config for Config {
crate::protocol::AuthType::RecurseCenter => {
let id = crate::oauth::load_client_auth_id(self.client.auth);
crate::protocol::Auth::recurse_center(
+ crate::protocol::AuthClient::Cli,
id.as_ref().map(std::string::String::as_str),
)
}
diff --git a/teleterm/src/config.rs b/teleterm/src/config.rs
index 7454a2a..4000cb7 100644
--- a/teleterm/src/config.rs
+++ b/teleterm/src/config.rs
@@ -872,51 +872,80 @@ pub fn oauth_configs<'a, D>(
) -> std::result::Result<
std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
D::Error,
>
where
D: serde::de::Deserializer<'a>,
{
- let configs =
- <std::collections::HashMap<String, OauthConfig>>::deserialize(
- deserializer,
- )?;
- let mut ret = std::collections::HashMap::new();
- for (key, config) in configs {
+ let configs = <std::collections::HashMap<
+ String,
+ std::collections::HashMap<String, OauthConfig>,
+ >>::deserialize(deserializer)?;
+ let mut all_configs = std::collections::HashMap::new();
+ for (key, client_configs) in configs {
let auth_type = crate::protocol::AuthType::try_from(key.as_str())
.map_err(serde::de::Error::custom)?;
- let real_config = match auth_type {
- crate::protocol::AuthType::RecurseCenter => {
- let client_id = config
- .client_id
- .context(crate::error::OauthMissingConfiguration {
- field: "client_id",
- auth_type,
- })
+ let mut auth_type_configs = std::collections::HashMap::new();
+ for (key, config) in client_configs {
+ let auth_client =
+ crate::protocol::AuthClient::try_from(key.as_str())
.map_err(serde::de::Error::custom)?;
- let client_secret = config
- .client_secret
- .context(crate::error::OauthMissingConfiguration {
- field: "client_secret",
- auth_type,
- })
- .map_err(serde::de::Error::custom)?;
- crate::oauth::RecurseCenter::config(
- &client_id,
- &client_secret,
- )
- }
- ty if !ty.is_oauth() => {
- return Err(Error::AuthTypeNotOauth { ty: auth_type })
- .map_err(serde::de::Error::custom);
- }
- _ => unreachable!(),
- };
- ret.insert(auth_type, real_config);
+ let real_config = match auth_type {
+ crate::protocol::AuthType::RecurseCenter => {
+ let client_id = config
+ .client_id
+ .context(crate::error::OauthMissingConfiguration {
+ field: "client_id",
+ auth_type,
+ auth_client,
+ })
+ .map_err(serde::de::Error::custom)?;
+ let client_secret = config
+ .client_secret
+ .context(crate::error::OauthMissingConfiguration {
+ field: "client_secret",
+ auth_type,
+ auth_client,
+ })
+ .map_err(serde::de::Error::custom)?;
+ let redirect_url =
+ if auth_client == crate::protocol::AuthClient::Cli {
+ url::Url::parse(crate::oauth::CLI_REDIRECT_URL)
+ .unwrap()
+ } else {
+ config
+ .redirect_url
+ .context(
+ crate::error::OauthMissingConfiguration {
+ field: "redirect_url",
+ auth_type,
+ auth_client,
+ },
+ )
+ .map_err(serde::de::Error::custom)?
+ };
+ crate::oauth::RecurseCenter::config(
+ &client_id,
+ &client_secret,
+ &redirect_url,
+ )
+ }
+ ty if !ty.is_oauth() => {
+ return Err(Error::AuthTypeNotOauth { ty: auth_type })
+ .map_err(serde::de::Error::custom);
+ }
+ _ => unreachable!(),
+ };
+ auth_type_configs.insert(auth_client, real_config);
+ }
+ all_configs.insert(auth_type, auth_type_configs);
}
- Ok(ret)
+ Ok(all_configs)
}
#[derive(serde::Deserialize, Debug)]
diff --git a/teleterm/src/error.rs b/teleterm/src/error.rs
index 9cc0ca7..f7946dd 100644
--- a/teleterm/src/error.rs
+++ b/teleterm/src/error.rs
@@ -107,6 +107,12 @@ pub enum Error {
#[snafu(display("failed to find any resolvable addresses"))]
HasResolvedAddr,
+ #[snafu(display("invalid auth client {}", ty))]
+ InvalidAuthClient { ty: u8 },
+
+ #[snafu(display("invalid auth client {}", ty))]
+ InvalidAuthClientStr { ty: String },
+
#[snafu(display("invalid auth type {}", ty))]
InvalidAuthType { ty: u8 },
@@ -143,13 +149,15 @@ pub enum Error {
NotAFileName { path: String },
#[snafu(display(
- "missing oauth configuration item {} for auth type {}",
+ "missing oauth configuration item {} for section oauth.{}.{}",
field,
auth_type.name(),
+ auth_client.name(),
))]
OauthMissingConfiguration {
field: String,
auth_type: crate::protocol::AuthType,
+ auth_client: crate::protocol::AuthClient,
},
#[snafu(display("failed to open file {}: {}", filename, source))]
diff --git a/teleterm/src/oauth.rs b/teleterm/src/oauth.rs
index 680b6f4..5283957 100644
--- a/teleterm/src/oauth.rs
+++ b/teleterm/src/oauth.rs
@@ -6,7 +6,7 @@ mod recurse_center;
pub use recurse_center::RecurseCenter;
// this needs to be fixed because we listen for it in a hardcoded place
-pub const REDIRECT_URL: &str = "http://localhost:44141/oauth";
+pub const CLI_REDIRECT_URL: &str = "http://localhost:44141/oauth";
pub trait Oauth {
fn client(&self) -> &oauth2::basic::BasicClient;
diff --git a/teleterm/src/oauth/recurse_center.rs b/teleterm/src/oauth/recurse_center.rs
index 6eeb69c..c43be15 100644
--- a/teleterm/src/oauth/recurse_center.rs
+++ b/teleterm/src/oauth/recurse_center.rs
@@ -13,7 +13,11 @@ impl RecurseCenter {
}
}
- pub fn config(client_id: &str, client_secret: &str) -> super::Config {
+ pub fn config(
+ client_id: &str,
+ client_secret: &str,
+ redirect_url: &url::Url,
+ ) -> super::Config {
super::Config {
client_id: client_id.to_string(),
client_secret: client_secret.to_string(),
@@ -23,7 +27,7 @@ impl RecurseCenter {
.unwrap(),
token_url: url::Url::parse("https://www.recurse.com/oauth/token")
.unwrap(),
- redirect_url: url::Url::parse(super::REDIRECT_URL).unwrap(),
+ redirect_url: redirect_url.clone(),
}
}
}
diff --git a/teleterm/src/protocol.rs b/teleterm/src/protocol.rs
index d2b7806..130e52d 100644
--- a/teleterm/src/protocol.rs
+++ b/teleterm/src/protocol.rs
@@ -52,7 +52,67 @@ impl<T: tokio::io::AsyncWrite> FramedWriter<T> {
pub const PROTO_VERSION: u8 = 1;
#[repr(u8)]
-#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
+#[derive(
+ Copy,
+ Clone,
+ Debug,
+ Eq,
+ Hash,
+ PartialEq,
+ serde::Deserialize,
+ serde::Serialize,
+)]
+pub enum AuthClient {
+ Cli = 0,
+ Web,
+}
+
+impl AuthClient {
+ pub fn name(self) -> &'static str {
+ match self {
+ Self::Cli => "cli",
+ Self::Web => "web",
+ }
+ }
+}
+
+impl std::convert::TryFrom<u8> for AuthClient {
+ type Error = Error;
+
+ fn try_from(n: u8) -> Result<Self> {
+ Ok(match n {
+ 0 => Self::Cli,
+ 1 => Self::Web,
+ _ => return Err(Error::InvalidAuthClient { ty: n }),
+ })
+ }
+}
+
+impl std::convert::TryFrom<&str> for AuthClient {
+ type Error = Error;
+
+ fn try_from(s: &str) -> Result<Self> {
+ Ok(match s {
+ s if Self::Cli.name() == s => Self::Cli,
+ s if Self::Web.name() == s => Self::Web,
+ _ => {
+ return Err(Error::InvalidAuthClientStr { ty: s.to_string() })
+ }
+ })
+ }
+}
+
+#[repr(u8)]
+#[derive(
+ Copy,
+ Clone,
+ Debug,
+ Eq,
+ Hash,
+ PartialEq,
+ serde::Deserialize,
+ serde::Serialize,
+)]
pub enum AuthType {
Plain = 0,
RecurseCenter,
@@ -107,8 +167,13 @@ impl std::convert::TryFrom<&str> for AuthType {
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub enum Auth {
- Plain { username: String },
- RecurseCenter { id: Option<String> },
+ Plain {
+ username: String,
+ },
+ RecurseCenter {
+ auth_client: AuthClient,
+ id: Option<String>,
+ },
}
impl Auth {
@@ -118,8 +183,9 @@ impl Auth {
}
}
- pub fn recurse_center(id: Option<&str>) -> Self {
+ pub fn recurse_center(auth_client: AuthClient, id: Option<&str>) -> Self {
Self::RecurseCenter {
+ auth_client,
id: id.map(std::string::ToString::to_string),
}
}
@@ -128,8 +194,13 @@ impl Auth {
self.auth_type().is_oauth()
}
- pub fn name(&self) -> &'static str {
- self.auth_type().name()
+ pub fn name(&self) -> String {
+ match &self {
+ Self::RecurseCenter { auth_client, .. } => {
+ format!("{}.{}", self.auth_type().name(), auth_client.name())
+ }
+ _ => self.auth_type().name().to_string(),
+ }
}
pub fn auth_type(&self) -> AuthType {
@@ -489,8 +560,9 @@ impl From<&Message> for Packet {
Auth::Plain { username } => {
write_str(username, data);
}
- Auth::RecurseCenter { id } => {
+ Auth::RecurseCenter { auth_client, id } => {
let id = id.as_ref().map_or("", |s| s.as_str());
+ write_u8(*auth_client as u8, data);
write_str(id, data);
}
}
@@ -656,9 +728,11 @@ impl std::convert::TryFrom<Packet> for Message {
(auth, data)
}
AuthType::RecurseCenter => {
+ let (auth_client, data) = read_u8(data)?;
+ let auth_client = AuthClient::try_from(auth_client)?;
let (id, data) = read_str(data)?;
let id = if id == "" { None } else { Some(id) };
- let auth = Auth::RecurseCenter { id };
+ let auth = Auth::RecurseCenter { auth_client, id };
(auth, data)
}
};
@@ -876,13 +950,17 @@ mod test {
),
Message::login(
&Auth::RecurseCenter {
+ auth_client: AuthClient::Cli,
id: Some("some-random-id".to_string()),
},
"screen",
crate::term::Size { rows: 24, cols: 80 },
),
Message::login(
- &Auth::RecurseCenter { id: None },
+ &Auth::RecurseCenter {
+ auth_client: AuthClient::Cli,
+ id: None,
+ },
"screen",
crate::term::Size { rows: 24, cols: 80 },
),
diff --git a/teleterm/src/server.rs b/teleterm/src/server.rs
index 1f565a2..e36f3fc 100644
--- a/teleterm/src/server.rs
+++ b/teleterm/src/server.rs
@@ -309,7 +309,10 @@ pub struct Server<
allowed_auth_types: std::collections::HashSet<crate::protocol::AuthType>,
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
}
@@ -324,7 +327,10 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
>,
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
) -> Self {
Self {
@@ -379,19 +385,28 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
));
}
oauth if oauth.is_oauth() => {
- let config = self.oauth_configs.get(&ty).context(
+ let configs = self.oauth_configs.get(&ty).context(
crate::error::AuthTypeMissingOauthConfig { ty },
)?;
let (refresh, client) = match oauth {
- crate::protocol::Auth::RecurseCenter { id } => (
- id.is_some(),
- Box::new(crate::oauth::RecurseCenter::new(
- config.clone(),
- &id.clone().unwrap_or_else(|| {
- format!("{}", uuid::Uuid::new_v4())
- }),
- )),
- ),
+ crate::protocol::Auth::RecurseCenter {
+ auth_client,
+ id,
+ ..
+ } => {
+ let config = configs.get(auth_client).context(
+ crate::error::AuthTypeMissingOauthConfig { ty },
+ )?;
+ (
+ id.is_some(),
+ Box::new(crate::oauth::RecurseCenter::new(
+ config.clone(),
+ &id.clone().unwrap_or_else(|| {
+ format!("{}", uuid::Uuid::new_v4())
+ }),
+ )),
+ )
+ }
_ => unreachable!(),
};
diff --git a/teleterm/src/server/tls.rs b/teleterm/src/server/tls.rs
index 28b9b28..694866a 100644
--- a/teleterm/src/server/tls.rs
+++ b/teleterm/src/server/tls.rs
@@ -28,7 +28,10 @@ impl Server {
>,
oauth_configs: std::collections::HashMap<
crate::protocol::AuthType,
- crate::oauth::Config,
+ std::collections::HashMap<
+ crate::protocol::AuthClient,
+ crate::oauth::Config,
+ >,
>,
) -> Self {
let (tls_sock_w, tls_sock_r) = tokio::sync::mpsc::channel(100);