don't suggest public key auth in the web auth API

This commit is contained in:
Eugene 2023-11-23 19:36:05 +01:00
parent e3b26b2699
commit b346ca3d0b
No known key found for this signature in database
GPG key ID: 5896FCBBDD1CF4F4
9 changed files with 66 additions and 19 deletions

View file

@ -5,7 +5,7 @@ use std::time::{Duration, Instant};
use once_cell::sync::Lazy;
use tokio::sync::{broadcast, Mutex};
use uuid::Uuid;
use warpgate_common::auth::{AuthResult, AuthState};
use warpgate_common::auth::{AuthResult, AuthState, CredentialKind};
use warpgate_common::{SessionId, WarpgateError};
use crate::ConfigProvider;
@ -52,13 +52,14 @@ impl AuthStateStore {
session_id: Option<&SessionId>,
username: &str,
protocol: &str,
supported_credential_types: &[CredentialKind],
) -> Result<(Uuid, Arc<Mutex<AuthState>>), WarpgateError> {
let id = Uuid::new_v4();
let policy = self
.config_provider
.lock()
.await
.get_credential_policy(username)
.get_credential_policy(username, supported_credential_types)
.await?;
let Some(policy) = policy else {
return Err(WarpgateError::UserNotFound)

View file

@ -63,6 +63,7 @@ impl ConfigProvider for DatabaseConfigProvider {
async fn get_credential_policy(
&mut self,
username: &str,
supported_credential_types: &[CredentialKind],
) -> Result<Option<Box<dyn CredentialPolicy + Sync + Send>>, WarpgateError> {
let db = self.db.lock().await;
@ -78,8 +79,12 @@ impl ConfigProvider for DatabaseConfigProvider {
let user: UserConfig = user_model.try_into()?;
let supported_credential_types: HashSet<CredentialKind> =
user.credentials.iter().map(|x| x.kind()).collect();
let supported_credential_types: HashSet<CredentialKind> = user
.credentials
.iter()
.map(|x| x.kind())
.filter(|x| supported_credential_types.contains(x))
.collect();
let default_policy = Box::new(AnySingleCredentialPolicy {
supported_credential_types: supported_credential_types.clone(),
}) as Box<dyn CredentialPolicy + Sync + Send>;

View file

@ -59,6 +59,7 @@ impl ConfigProvider for FileConfigProvider {
async fn get_credential_policy(
&mut self,
username: &str,
supported_credential_types: &[CredentialKind],
) -> Result<Option<Box<dyn CredentialPolicy + Sync + Send>>, WarpgateError> {
let user = {
self.config
@ -75,8 +76,12 @@ impl ConfigProvider for FileConfigProvider {
return Ok(None);
};
let supported_credential_types: HashSet<CredentialKind> =
user.credentials.iter().map(|x| x.kind()).collect();
let supported_credential_types: HashSet<CredentialKind> = user
.credentials
.iter()
.map(|x| x.kind())
.filter(|x| supported_credential_types.contains(x))
.collect();
let default_policy = Box::new(AnySingleCredentialPolicy {
supported_credential_types: supported_credential_types.clone(),
}) as Box<dyn CredentialPolicy + Sync + Send>;

View file

@ -10,7 +10,7 @@ use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, Qu
use tokio::sync::Mutex;
use tracing::*;
use uuid::Uuid;
use warpgate_common::auth::{AuthCredential, CredentialPolicy};
use warpgate_common::auth::{AuthCredential, CredentialPolicy, CredentialKind};
use warpgate_common::{Secret, Target, User, WarpgateError};
use warpgate_db_entities::Ticket;
@ -34,6 +34,7 @@ pub trait ConfigProvider {
async fn get_credential_policy(
&mut self,
username: &str,
supported_credential_types: &[CredentialKind],
) -> Result<Option<Box<dyn CredentialPolicy + Sync + Send>>, WarpgateError>;
async fn authorize_target(

View file

@ -81,18 +81,32 @@ enum AuthStateResponse {
NotFound,
}
const PREFERRED_NEED_CRED_ORDER: &[CredentialKind] = &[
CredentialKind::PublicKey,
CredentialKind::Password,
CredentialKind::Totp,
CredentialKind::Sso,
CredentialKind::WebUserApproval,
];
impl From<AuthResult> for ApiAuthState {
fn from(state: AuthResult) -> Self {
match state {
AuthResult::Rejected => ApiAuthState::Failed,
AuthResult::Need(kinds) => match kinds.iter().next() {
Some(CredentialKind::Password) => ApiAuthState::PasswordNeeded,
Some(CredentialKind::Totp) => ApiAuthState::OtpNeeded,
Some(CredentialKind::Sso) => ApiAuthState::SsoNeeded,
Some(CredentialKind::WebUserApproval) => ApiAuthState::WebUserApprovalNeeded,
Some(CredentialKind::PublicKey) => ApiAuthState::PublicKeyNeeded,
None => ApiAuthState::Failed,
},
AuthResult::Need(kinds) => {
let kind = PREFERRED_NEED_CRED_ORDER
.iter()
.find(|x| kinds.contains(x))
.or(kinds.iter().next());
match kind {
Some(CredentialKind::Password) => ApiAuthState::PasswordNeeded,
Some(CredentialKind::Totp) => ApiAuthState::OtpNeeded,
Some(CredentialKind::Sso) => ApiAuthState::SsoNeeded,
Some(CredentialKind::WebUserApproval) => ApiAuthState::WebUserApprovalNeeded,
Some(CredentialKind::PublicKey) => ApiAuthState::PublicKeyNeeded,
None => ApiAuthState::Failed,
}
}
AuthResult::Accepted { .. } => ApiAuthState::Success,
}
}

View file

@ -9,7 +9,7 @@ use poem::{Endpoint, EndpointExt, FromRequest, IntoResponse, Request, Response};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use uuid::Uuid;
use warpgate_common::auth::AuthState;
use warpgate_common::auth::{AuthState, CredentialKind};
use warpgate_common::{ProtocolName, TargetOptions, WarpgateError};
use warpgate_core::{AuthStateStore, Services};
@ -204,7 +204,16 @@ pub async fn get_auth_state_for_request(
}
let (id, state) = store
.create(None, username, crate::common::PROTOCOL_NAME)
.create(
None,
username,
crate::common::PROTOCOL_NAME,
&[
CredentialKind::Password,
CredentialKind::Sso,
CredentialKind::Totp,
],
)
.await?;
session.set(AUTH_STATE_ID_SESSION_KEY, AuthStateId(id));
Ok(state)

View file

@ -8,7 +8,7 @@ use tokio::net::TcpStream;
use tokio::sync::Mutex;
use tracing::*;
use uuid::Uuid;
use warpgate_common::auth::{AuthCredential, AuthResult, AuthSelector};
use warpgate_common::auth::{AuthCredential, AuthResult, AuthSelector, CredentialKind};
use warpgate_common::helpers::rng::get_crypto_rng;
use warpgate_common::{Secret, TargetMySqlOptions, TargetOptions};
use warpgate_core::{authorize_ticket, consume_ticket, Services, WarpgateServerHandle};
@ -194,6 +194,7 @@ impl MySqlSession {
Some(&self.server_handle.lock().await.id()),
&username,
crate::common::PROTOCOL_NAME,
&[CredentialKind::Password],
)
.await?
.1;

View file

@ -228,7 +228,17 @@ impl ServerSession {
.auth_state_store
.lock()
.await
.create(Some(&self.id), username, crate::PROTOCOL_NAME)
.create(
Some(&self.id),
username,
crate::PROTOCOL_NAME,
&[
CredentialKind::Password,
CredentialKind::PublicKey,
CredentialKind::Totp,
CredentialKind::WebUserApproval,
],
)
.await?
.1;
self.auth_state = Some(state);

View file

@ -120,6 +120,7 @@ async function cancel () {
function onInputKey (event: KeyboardEvent) {
if (event.key === 'Enter') {
login()
event.preventDefault()
}
}