forked from lavina/lavina
[xmpp] logopass auth
This commit is contained in:
parent
53f218c58f
commit
da520f5a25
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())?)
|
||||||
|
|
|
@ -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""#);
|
||||||
|
|
Loading…
Reference in New Issue