Skip to content

Commit

Permalink
fixed #854 - show session details during OOB auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugeny committed Aug 7, 2023
1 parent fc1a93b commit 0bc9ae1
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 30 deletions.
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions warpgate-common/src/auth/state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::collections::HashSet;

use chrono::{DateTime, Utc};
use rand::Rng;
use uuid::Uuid;

use super::{AuthCredential, CredentialKind, CredentialPolicy, CredentialPolicyResponse};
use crate::SessionId;

#[derive(Debug, Clone)]
pub enum AuthResult {
Expand All @@ -13,34 +16,54 @@ pub enum AuthResult {

pub struct AuthState {
id: Uuid,
session_id: Option<Uuid>,
username: String,
protocol: String,
force_rejected: bool,
policy: Box<dyn CredentialPolicy + Sync + Send>,
valid_credentials: Vec<AuthCredential>,
started: DateTime<Utc>,
identification_string: String,
}

fn generate_identification_string() -> String {
let mut s = String::new();
let mut rng = rand::thread_rng();
for _ in 0..4 {
s.push_str(&format!("{:X}", rng.gen_range(0..16)));
}
s
}

impl AuthState {
pub fn new(
id: Uuid,
session_id: Option<SessionId>,
username: String,
protocol: String,
policy: Box<dyn CredentialPolicy + Sync + Send>,
) -> Self {
Self {
id,
session_id,
username,
protocol,
force_rejected: false,
policy,
valid_credentials: vec![],
started: Utc::now(),
identification_string: generate_identification_string(),
}
}

pub fn id(&self) -> &Uuid {
&self.id
}

pub fn session_id(&self) -> &Option<SessionId> {
&self.session_id
}

pub fn username(&self) -> &str {
&self.username
}
Expand All @@ -49,6 +72,14 @@ impl AuthState {
&self.protocol
}

pub fn started(&self) -> &DateTime<Utc> {
&self.started
}

pub fn identification_string(&self) -> &str {
&self.identification_string
}

pub fn add_valid_credential(&mut self, credential: AuthCredential) {
self.valid_credentials.push(credential);
}
Expand Down
11 changes: 9 additions & 2 deletions warpgate-core/src/auth_state_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use once_cell::sync::Lazy;
use tokio::sync::{broadcast, Mutex};
use uuid::Uuid;
use warpgate_common::auth::{AuthResult, AuthState};
use warpgate_common::WarpgateError;
use warpgate_common::{WarpgateError, SessionId};

use crate::ConfigProvider;

Expand Down Expand Up @@ -49,6 +49,7 @@ impl AuthStateStore {

pub async fn create(
&mut self,
session_id: Option<&SessionId>,
username: &str,
protocol: &str,
) -> Result<(Uuid, Arc<Mutex<AuthState>>), WarpgateError> {
Expand All @@ -63,7 +64,13 @@ impl AuthStateStore {
return Err(WarpgateError::UserNotFound)
};

let state = AuthState::new(id, username.to_string(), protocol.to_string(), policy);
let state = AuthState::new(
id,
session_id.copied(),
username.to_string(),
protocol.to_string(),
policy,
);
self.store
.insert(id, (Arc::new(Mutex::new(state)), Instant::now()));

Expand Down
29 changes: 24 additions & 5 deletions warpgate-protocol-http/src/api/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Arc;

use chrono::{DateTime, Utc};
use poem::session::Session;
use poem::web::Data;
use poem::Request;
Expand Down Expand Up @@ -66,7 +67,10 @@ enum LogoutResponse {
#[derive(Object)]
struct AuthStateResponseInternal {
pub protocol: String,
pub address: Option<String>,
pub started: DateTime<Utc>,
pub state: ApiAuthState,
pub identification_string: String,
}

#[derive(ApiResponse)]
Expand Down Expand Up @@ -214,7 +218,7 @@ impl Api {
let Some(state_arc) = store.get(&state_id.0) else {
return Ok(AuthStateResponse::NotFound);
};
serialize_auth_state_inner(state_arc).await
serialize_auth_state_inner(state_arc, *services).await
}

#[oai(
Expand All @@ -237,7 +241,7 @@ impl Api {
state_arc.lock().await.reject();
store.complete(&state_id.0).await;
session.clear_auth_state();
serialize_auth_state_inner(state_arc).await
serialize_auth_state_inner(state_arc, *services).await
}

#[oai(
Expand All @@ -256,7 +260,7 @@ impl Api {
let Some(state_arc) = state_arc else {
return Ok(AuthStateResponse::NotFound);
};
serialize_auth_state_inner(state_arc).await
serialize_auth_state_inner(state_arc, *services).await
}

#[oai(
Expand Down Expand Up @@ -284,7 +288,7 @@ impl Api {
if let AuthResult::Accepted { .. } = auth_result {
services.auth_state_store.lock().await.complete(&id).await;
}
serialize_auth_state_inner(state_arc).await
serialize_auth_state_inner(state_arc, *services).await
}

#[oai(
Expand All @@ -304,7 +308,7 @@ impl Api {
};
state_arc.lock().await.reject();
services.auth_state_store.lock().await.complete(&id).await;
serialize_auth_state_inner(state_arc).await
serialize_auth_state_inner(state_arc, *services).await
}
}

Expand Down Expand Up @@ -339,10 +343,25 @@ async fn get_auth_state(

async fn serialize_auth_state_inner(
state_arc: Arc<Mutex<AuthState>>,
services: &Services,
) -> poem::Result<AuthStateResponse> {
let state = state_arc.lock().await;

let session_state_store = services.state.lock().await;
let session_state = state
.session_id()
.and_then(|session_id| session_state_store.sessions.get(&session_id));

let peer_addr = match session_state {
Some(x) => x.lock().await.remote_address,
None => None,
};

Ok(AuthStateResponse::Ok(Json(AuthStateResponseInternal {
protocol: state.protocol().to_string(),
address: peer_addr.map(|x| x.ip().to_string()),
started: state.started().clone(),
state: state.verify().into(),
identification_string: state.identification_string().to_owned(),
})))
}
4 changes: 3 additions & 1 deletion warpgate-protocol-http/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ pub async fn get_auth_state_for_request(
match session.get_auth_state_id() {
Some(id) => Ok(store.get(&id.0).ok_or(WarpgateError::InconsistentState)?),
None => {
let (id, state) = store.create(username, crate::common::PROTOCOL_NAME).await?;
let (id, state) = store
.create(None, username, crate::common::PROTOCOL_NAME)
.await?;
session.set(AUTH_STATE_ID_SESSION_KEY, AuthStateId(id));
Ok(state)
}
Expand Down
6 changes: 5 additions & 1 deletion warpgate-protocol-mysql/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ impl MySqlSession {
.auth_state_store
.lock()
.await
.create(&username, crate::common::PROTOCOL_NAME)
.create(
Some(&self.server_handle.lock().await.id()),
&username,
crate::common::PROTOCOL_NAME,
)
.await?
.1;
let mut state = state_arc.lock().await;
Expand Down
20 changes: 15 additions & 5 deletions warpgate-protocol-ssh/src/server/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl ServerSession {
.auth_state_store
.lock()
.await
.create(username, crate::PROTOCOL_NAME)
.create(Some(&self.id), username, crate::PROTOCOL_NAME)
.await?
.1;
self.auth_state = Some(state);
Expand Down Expand Up @@ -1279,6 +1279,8 @@ impl ServerSession {
let Some(auth_state) = self.auth_state.as_ref() else {
return russh::server::Auth::Reject { proceed_with_methods: None};
};
let identification_string =
auth_state.lock().await.identification_string().to_owned();
let auth_state_id = *auth_state.lock().await.id();
let event = self
.services
Expand Down Expand Up @@ -1311,11 +1313,19 @@ impl ServerSession {
russh::server::Auth::Partial {
name: Cow::Owned(format!(
concat!(
"----------------------------------------------------------------\n",
"Warpgate authentication: please open {} in your browser\n",
"----------------------------------------------------------------\n"
"-----------------------------------------------------------------------\n",
"Warpgate authentication: please open the following URL in your browser:\n",
"{}\n\n",
"Make sure you're seeing this security key: {}\n",
"-----------------------------------------------------------------------\n"
),
login_url
login_url,
identification_string
.chars()
.into_iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(" ")
)),
instructions: Cow::Borrowed(""),
prompts: Cow::Owned(vec![(Cow::Borrowed("Press Enter when done: "), true)]),
Expand Down
2 changes: 1 addition & 1 deletion warpgate-web/src/admin/lib/openapi-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.0.0",
"info": {
"title": "Warpgate Web Admin",
"version": "0.7.1"
"version": "0.7.4"
},
"servers": [
{
Expand Down
Loading

0 comments on commit 0bc9ae1

Please sign in to comment.