[xmpp] logopass auth

This commit is contained in:
JustTestingV 2023-10-01 22:15:44 +03:00
parent 53f218c58f
commit da520f5a25
5 changed files with 86 additions and 30 deletions

1
Cargo.lock generated
View File

@ -754,6 +754,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_matches", "assert_matches",
"base64",
"derive_more", "derive_more",
"figment", "figment",
"futures-util", "futures-util",

View File

@ -25,6 +25,7 @@ quick-xml = { version = "0.30.0", features = ["async-tokio"] }
derive_more = "0.99.17" derive_more = "0.99.17"
uuid = { version = "1.3.0", features = ["v4"] } uuid = { version = "1.3.0", features = ["v4"] }
sqlx = { version = "0.7.0-alpha.2", features = ["sqlite", "runtime-tokio-rustls", "migrate"] } sqlx = { version = "0.7.0-alpha.2", features = ["sqlite", "runtime-tokio-rustls", "migrate"] }
base64 = "0.21.3"
[dev-dependencies] [dev-dependencies]
assert_matches = "1.5.0" assert_matches = "1.5.0"

View File

@ -58,7 +58,7 @@ async fn main() -> Result<()> {
let telemetry_terminator = let telemetry_terminator =
util::telemetry::launch(telemetry_config, metrics.clone(), rooms.clone()).await?; util::telemetry::launch(telemetry_config, metrics.clone(), rooms.clone()).await?;
let irc = projections::irc::launch(irc_config, players.clone(), rooms.clone(), metrics.clone(), storage.clone()).await?; let irc = projections::irc::launch(irc_config, players.clone(), rooms.clone(), metrics.clone(), storage.clone()).await?;
let xmpp = projections::xmpp::launch(xmpp_config, players.clone(), rooms.clone(), metrics.clone()).await?; let xmpp = projections::xmpp::launch(xmpp_config, players.clone(), rooms.clone(), metrics.clone(), storage.clone()).await?;
tracing::info!("Started"); tracing::info!("Started");
sleep.await; sleep.await;

View File

@ -20,6 +20,7 @@ use tokio_rustls::rustls::{Certificate, PrivateKey};
use tokio_rustls::TlsAcceptor; use tokio_rustls::TlsAcceptor;
use crate::core::player::{PlayerConnection, PlayerId, PlayerRegistry}; use crate::core::player::{PlayerConnection, PlayerId, PlayerRegistry};
use crate::core::repo::Storage;
use crate::core::room::{RoomId, RoomRegistry}; use crate::core::room::{RoomId, RoomRegistry};
use crate::prelude::*; use crate::prelude::*;
use crate::protos::xmpp; use crate::protos::xmpp;
@ -27,6 +28,7 @@ use crate::protos::xmpp::bind::{BindResponse, Jid, Name, Resource, Server};
use crate::protos::xmpp::client::{Iq, Message, MessageType, Presence}; use crate::protos::xmpp::client::{Iq, Message, MessageType, Presence};
use crate::protos::xmpp::disco::*; use crate::protos::xmpp::disco::*;
use crate::protos::xmpp::roster::RosterQuery; use crate::protos::xmpp::roster::RosterQuery;
use crate::protos::xmpp::sasl::AuthBody;
use crate::protos::xmpp::session::Session; use crate::protos::xmpp::session::Session;
use crate::protos::xmpp::stream::*; use crate::protos::xmpp::stream::*;
use crate::util::xml::{Continuation, FromXml, Parser, ToXml}; use crate::util::xml::{Continuation, FromXml, Parser, ToXml};
@ -58,6 +60,7 @@ pub async fn launch(
players: PlayerRegistry, players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
metrics: MetricsRegistry, metrics: MetricsRegistry,
storage: Storage,
) -> Result<Terminator> { ) -> Result<Terminator> {
log::info!("Starting XMPP projection"); log::info!("Starting XMPP projection");
@ -97,11 +100,12 @@ pub async fn launch(
} }
let players = players.clone(); let players = players.clone();
let rooms = rooms.clone(); let rooms = rooms.clone();
let storage = storage.clone();
let terminator = Terminator::spawn(|termination| { let terminator = Terminator::spawn(|termination| {
let stopped_tx = stopped_tx.clone(); let stopped_tx = stopped_tx.clone();
let loaded_config = loaded_config.clone(); let loaded_config = loaded_config.clone();
async move { async move {
match handle_socket(loaded_config, stream, &socket_addr, players, rooms, termination).await { match handle_socket(loaded_config, stream, &socket_addr, players, rooms, storage, termination).await {
Ok(_) => log::info!("Connection terminated"), Ok(_) => log::info!("Connection terminated"),
Err(err) => log::warn!("Connection failed: {err}"), Err(err) => log::warn!("Connection failed: {err}"),
} }
@ -133,7 +137,7 @@ pub async fn launch(
} }
}), }),
) )
.await; .await;
log::info!("Stopped XMPP projection"); log::info!("Stopped XMPP projection");
Ok(()) Ok(())
}); });
@ -147,6 +151,7 @@ async fn handle_socket(
socket_addr: &SocketAddr, socket_addr: &SocketAddr,
mut players: PlayerRegistry, mut players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
storage: Storage,
termination: Deferred<()>, // TODO use it to stop the connection gracefully termination: Deferred<()>, // TODO use it to stop the connection gracefully
) -> Result<()> { ) -> Result<()> {
log::debug!("Received an XMPP connection from {socket_addr}"); log::debug!("Received an XMPP connection from {socket_addr}");
@ -171,7 +176,8 @@ async fn handle_socket(
let mut xml_reader = NsReader::from_reader(BufReader::new(a)); let mut xml_reader = NsReader::from_reader(BufReader::new(a));
let mut xml_writer = Writer::new(b); let mut xml_writer = Writer::new(b);
let authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf).await?; let mut storage = storage.clone();
let authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf, &mut storage).await?;
let mut connection = players let mut connection = players
.connect_to_player(authenticated.player_id.clone()) .connect_to_player(authenticated.player_id.clone())
.await; .await;
@ -183,7 +189,7 @@ async fn handle_socket(
&mut connection, &mut connection,
&rooms, &rooms,
) )
.await?; .await?;
let a = xml_reader.into_inner().into_inner(); let a = xml_reader.into_inner().into_inner();
let b = xml_writer.into_inner(); let b = xml_writer.into_inner();
@ -229,6 +235,7 @@ async fn socket_auth(
xml_reader: &mut NsReader<(impl AsyncBufRead + Unpin)>, xml_reader: &mut NsReader<(impl AsyncBufRead + Unpin)>,
xml_writer: &mut Writer<(impl AsyncWrite + Unpin)>, xml_writer: &mut Writer<(impl AsyncWrite + Unpin)>,
reader_buf: &mut Vec<u8>, reader_buf: &mut Vec<u8>,
storage: &mut Storage,
) -> Result<Authenticated> { ) -> Result<Authenticated> {
read_xml_header(xml_reader, reader_buf).await?; read_xml_header(xml_reader, reader_buf).await?;
let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?; let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?;
@ -242,27 +249,52 @@ async fn socket_auth(
id: uuid::Uuid::new_v4().to_string(), id: uuid::Uuid::new_v4().to_string(),
version: "1.0".into(), version: "1.0".into(),
} }
.write_xml(xml_writer) .write_xml(xml_writer)
.await?; .await?;
Features { Features {
start_tls: false, start_tls: false,
mechanisms: true, mechanisms: true,
bind: false, bind: false,
} }
.write_xml(xml_writer) .write_xml(xml_writer)
.await?; .await?;
xml_writer.get_mut().flush().await?; xml_writer.get_mut().flush().await?;
let _ = xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?; let auth: xmpp::sasl::Auth = xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?;
xmpp::sasl::Success.write_xml(xml_writer).await?; xmpp::sasl::Success.write_xml(xml_writer).await?;
let name: Str = "darova".into(); let auth_body = AuthBody::from_str(auth.body);
Ok(Authenticated {
player_id: PlayerId::from("darova")?, match auth_body {
xmpp_name: Name(name.clone()), Ok(logopass) => {
xmpp_resource: Resource(name.clone()), let name = &logopass.login;
xmpp_muc_name: Resource(name), let stored_user = storage.retrieve_user_by_name(name).await?;
})
let stored_user = match stored_user {
Some(u) => u,
None => {
log::info!("User '{}' not found", name);
return Err(fail("no user found"));
}
};
if stored_user.password.is_none() {
log::info!("Password not defined for user '{}'", name);
return Err(fail("password is not defined"));
}
if stored_user.password.as_deref() != Some(&logopass.password) {
log::info!("Incorrect password supplied for user '{}'", name);
return Err(fail("passwords do not match"));
}
Ok(Authenticated {
player_id: PlayerId::from("darova")?,
xmpp_name: Name(name.to_string().into()),
xmpp_resource: Resource(name.to_string().into()),
xmpp_muc_name: Resource(name.to_string().into()),
})
}
Err(e) => Err(e)
}
} }
async fn socket_final( async fn socket_final(
@ -285,15 +317,15 @@ async fn socket_final(
id: uuid::Uuid::new_v4().to_string(), id: uuid::Uuid::new_v4().to_string(),
version: "1.0".into(), version: "1.0".into(),
} }
.write_xml(xml_writer) .write_xml(xml_writer)
.await?; .await?;
Features { Features {
start_tls: false, start_tls: false,
mechanisms: false, mechanisms: false,
bind: true, bind: true,
} }
.write_xml(xml_writer) .write_xml(xml_writer)
.await?; .await?;
xml_writer.get_mut().flush().await?; xml_writer.get_mut().flush().await?;
let mut parser = proto::ClientPacket::parse(); let mut parser = proto::ClientPacket::parse();
@ -390,10 +422,10 @@ async fn handle_packet(
} }
proto::ClientPacket::Message(m) => { proto::ClientPacket::Message(m) => {
if let Some(Jid { if let Some(Jid {
name: Some(name), name: Some(name),
server, server,
resource: _, resource: _,
}) = m.to }) = m.to
{ {
if server.0.as_ref() == "rooms.localhost" && m.r#type == MessageType::Groupchat { if server.0.as_ref() == "rooms.localhost" && m.r#type == MessageType::Groupchat {
user_handle user_handle
@ -419,7 +451,7 @@ async fn handle_packet(
subject: None, subject: None,
body: m.body.clone(), body: m.body.clone(),
} }
.serialize(output); .serialize(output);
false false
} else { } else {
todo!() todo!()
@ -444,10 +476,10 @@ async fn handle_packet(
..Default::default() ..Default::default()
} }
} else if let Some(Jid { } else if let Some(Jid {
name: Some(name), name: Some(name),
server, server,
resource: Some(resource), resource: Some(resource),
}) = p.to }) = p.to
{ {
let a = user_handle let a = user_handle
.join_room(RoomId::from(name.0.clone())?) .join_room(RoomId::from(name.0.clone())?)

View File

@ -12,6 +12,7 @@ use crate::prelude::*;
pub enum Mechanism { pub enum Mechanism {
Plain, Plain,
} }
impl Mechanism { impl Mechanism {
pub fn to_str(&self) -> &'static str { pub fn to_str(&self) -> &'static str {
match self { match self {
@ -27,10 +28,30 @@ impl Mechanism {
} }
} }
pub struct AuthBody {
pub login: String,
pub password: String,
}
use base64::{Engine as _, engine::general_purpose};
impl AuthBody {
pub fn from_str(input: Vec<u8>) -> Result<AuthBody> {
let decoded_body = general_purpose::STANDARD.decode(input).unwrap();
let parsed_to_string = String::from_utf8(decoded_body).unwrap();
let separated_words: Vec<&str> = parsed_to_string.split("\x00").filter(|&part| !part.is_empty()).collect::<Vec<_>>().clone();
if &separated_words.len() == &2_usize {
Ok(AuthBody { login: separated_words[0].to_string(), password: separated_words[1].to_string() })
} else { Err(fail("Incorrect auth format")) }
}
}
pub struct Auth { pub struct Auth {
pub mechanism: Mechanism, pub mechanism: Mechanism,
pub body: Vec<u8>, pub body: Vec<u8>,
} }
impl Auth { impl Auth {
pub async fn parse( pub async fn parse(
reader: &mut NsReader<impl AsyncBufRead + Unpin>, reader: &mut NsReader<impl AsyncBufRead + Unpin>,
@ -69,6 +90,7 @@ impl Auth {
} }
pub struct Success; pub struct Success;
impl Success { impl Success {
pub async fn write_xml(&self, writer: &mut Writer<impl AsyncWrite + Unpin>) -> Result<()> { pub async fn write_xml(&self, writer: &mut Writer<impl AsyncWrite + Unpin>) -> Result<()> {
let event = BytesStart::new(r#"success xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#); let event = BytesStart::new(r#"success xmlns="urn:ietf:params:xml:ns:xmpp-sasl""#);