Merge branch 'account' into 'master'

Fix account and media bugs

See merge request famedly/conduit!97
next
Timo Kösters 2021-06-08 07:22:32 +00:00
commit 81715bd84d
6 changed files with 93 additions and 45 deletions

View File

@ -179,12 +179,11 @@ pub async fn register_route(
let password = if is_guest { let password = if is_guest {
None None
} else { } else {
body.password.clone() body.password.as_deref()
} };
.unwrap_or_default();
// Create user // Create user
db.users.create(&user_id, &password)?; db.users.create(&user_id, password)?;
// Initial data // Initial data
db.account_data.update( db.account_data.update(
@ -233,7 +232,7 @@ pub async fn register_route(
let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name()) let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
.expect("@conduit:server_name is valid"); .expect("@conduit:server_name is valid");
db.users.create(&conduit_user, "")?; db.users.create(&conduit_user, None)?;
let room_id = RoomId::new(db.globals.server_name()); let room_id = RoomId::new(db.globals.server_name());
@ -547,7 +546,8 @@ pub async fn change_password_route(
return Err(Error::Uiaa(uiaainfo)); return Err(Error::Uiaa(uiaainfo));
} }
db.users.set_password(&sender_user, &body.new_password)?; db.users
.set_password(&sender_user, Some(&body.new_password))?;
if body.logout_devices { if body.logout_devices {
// Logout all devices except the current one // Logout all devices except the current one

View File

@ -38,7 +38,11 @@ pub async fn create_content_route(
); );
db.media.create( db.media.create(
mxc.clone(), mxc.clone(),
&body.filename.as_deref(), &body
.filename
.as_ref()
.map(|filename| "inline; filename=".to_owned() + filename)
.as_deref(),
&body.content_type.as_deref(), &body.content_type.as_deref(),
&body.file, &body.file,
)?; )?;
@ -64,7 +68,7 @@ pub async fn get_content_route(
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id); let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
if let Some(FileMeta { if let Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file, file,
}) = db.media.get(&mxc)? }) = db.media.get(&mxc)?
@ -72,7 +76,7 @@ pub async fn get_content_route(
Ok(get_content::Response { Ok(get_content::Response {
file, file,
content_type, content_type,
content_disposition: filename, content_disposition,
} }
.into()) .into())
} else if &*body.server_name != db.globals.server_name() && body.allow_remote { } else if &*body.server_name != db.globals.server_name() && body.allow_remote {

View File

@ -11,7 +11,7 @@ pub mod transaction_ids;
pub mod uiaa; pub mod uiaa;
pub mod users; pub mod users;
use crate::{Error, Result}; use crate::{utils, Error, Result};
use directories::ProjectDirs; use directories::ProjectDirs;
use futures::StreamExt; use futures::StreamExt;
use log::{error, info}; use log::{error, info};
@ -246,6 +246,25 @@ impl Database {
info!("Migration: 0 -> 1 finished"); info!("Migration: 0 -> 1 finished");
} }
if db.globals.database_version()? < 2 {
// We accidentally inserted hashed versions of "" into the db instead of just ""
for userid_password in db.users.userid_password.iter() {
let (userid, password) = userid_password?;
let password = utils::string_from_bytes(&password);
if password.map_or(false, |password| {
argon2::verify_encoded(&password, b"").unwrap_or(false)
}) {
db.users.userid_password.insert(userid, b"")?;
}
}
db.globals.bump_database_version(2)?;
info!("Migration: 1 -> 2 finished");
}
// This data is probably outdated // This data is probably outdated
db.rooms.edus.presenceid_presence.clear()?; db.rooms.edus.presenceid_presence.clear()?;

View File

@ -4,14 +4,14 @@ use crate::{utils, Error, Result};
use std::mem; use std::mem;
pub struct FileMeta { pub struct FileMeta {
pub filename: Option<String>, pub content_disposition: Option<String>,
pub content_type: Option<String>, pub content_type: Option<String>,
pub file: Vec<u8>, pub file: Vec<u8>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Media { pub struct Media {
pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + Filename + ContentType pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
} }
impl Media { impl Media {
@ -19,7 +19,7 @@ impl Media {
pub fn create( pub fn create(
&self, &self,
mxc: String, mxc: String,
filename: &Option<&str>, content_disposition: &Option<&str>,
content_type: &Option<&str>, content_type: &Option<&str>,
file: &[u8], file: &[u8],
) -> Result<()> { ) -> Result<()> {
@ -28,7 +28,12 @@ impl Media {
key.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail key.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
key.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail key.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
key.push(0xff); key.push(0xff);
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default()); key.extend_from_slice(
content_disposition
.as_ref()
.map(|f| f.as_bytes())
.unwrap_or_default(),
);
key.push(0xff); key.push(0xff);
key.extend_from_slice( key.extend_from_slice(
content_type content_type
@ -46,7 +51,7 @@ impl Media {
pub fn upload_thumbnail( pub fn upload_thumbnail(
&self, &self,
mxc: String, mxc: String,
filename: &Option<String>, content_disposition: &Option<String>,
content_type: &Option<String>, content_type: &Option<String>,
width: u32, width: u32,
height: u32, height: u32,
@ -57,7 +62,12 @@ impl Media {
key.extend_from_slice(&width.to_be_bytes()); key.extend_from_slice(&width.to_be_bytes());
key.extend_from_slice(&height.to_be_bytes()); key.extend_from_slice(&height.to_be_bytes());
key.push(0xff); key.push(0xff);
key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default()); key.extend_from_slice(
content_disposition
.as_ref()
.map(|f| f.as_bytes())
.unwrap_or_default(),
);
key.push(0xff); key.push(0xff);
key.extend_from_slice( key.extend_from_slice(
content_type content_type
@ -92,20 +102,24 @@ impl Media {
}) })
.transpose()?; .transpose()?;
let filename_bytes = parts let content_disposition_bytes = parts
.next() .next()
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
let filename = if filename_bytes.is_empty() { let content_disposition = if content_disposition_bytes.is_empty() {
None None
} else { } else {
Some(utils::string_from_bytes(filename_bytes).map_err(|_| { Some(
Error::bad_database("Filename in mediaid_file is invalid unicode.") utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
})?) Error::bad_database(
"Content Disposition in mediaid_file is invalid unicode.",
)
})?,
)
}; };
Ok(Some(FileMeta { Ok(Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file: file.to_vec(), file: file.to_vec(),
})) }))
@ -169,21 +183,22 @@ impl Media {
}) })
.transpose()?; .transpose()?;
let filename_bytes = parts let content_disposition_bytes = parts
.next() .next()
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
let filename = if filename_bytes.is_empty() { let content_disposition = if content_disposition_bytes.is_empty() {
None None
} else { } else {
Some( Some(
utils::string_from_bytes(filename_bytes) utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
.map_err(|_| Error::bad_database("Filename in db is invalid."))?, Error::bad_database("Content Disposition in db is invalid.")
})?,
) )
}; };
Ok(Some(FileMeta { Ok(Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file: file.to_vec(), file: file.to_vec(),
})) }))
@ -202,16 +217,20 @@ impl Media {
}) })
.transpose()?; .transpose()?;
let filename_bytes = parts let content_disposition_bytes = parts
.next() .next()
.ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?; .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
let filename = if filename_bytes.is_empty() { let content_disposition = if content_disposition_bytes.is_empty() {
None None
} else { } else {
Some(utils::string_from_bytes(filename_bytes).map_err(|_| { Some(
Error::bad_database("Filename in mediaid_file is invalid unicode.") utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
})?) Error::bad_database(
"Content Disposition in mediaid_file is invalid unicode.",
)
})?,
)
}; };
if let Ok(image) = image::load_from_memory(&file) { if let Ok(image) = image::load_from_memory(&file) {
@ -219,7 +238,7 @@ impl Media {
let original_height = image.height(); let original_height = image.height();
if width > original_width || height > original_height { if width > original_width || height > original_height {
return Ok(Some(FileMeta { return Ok(Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file: file.to_vec(), file: file.to_vec(),
})); }));
@ -286,14 +305,14 @@ impl Media {
self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?; self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?;
Ok(Some(FileMeta { Ok(Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file: thumbnail_bytes.to_vec(), file: thumbnail_bytes.to_vec(),
})) }))
} else { } else {
// Couldn't parse file to generate thumbnail, send original // Couldn't parse file to generate thumbnail, send original
Ok(Some(FileMeta { Ok(Some(FileMeta {
filename, content_disposition,
content_type, content_type,
file: file.to_vec(), file: file.to_vec(),
})) }))

View File

@ -725,8 +725,9 @@ impl Rooms {
.users .users
.iter() .iter()
.filter_map(|r| r.ok()) .filter_map(|r| r.ok())
.filter(|user_id| self.is_joined(&user_id, &pdu.room_id).unwrap_or(false)) .filter(|user_id| user_id.server_name() == db.globals.server_name())
.filter(|user_id| !db.users.is_deactivated(user_id).unwrap_or(false)) .filter(|user_id| !db.users.is_deactivated(user_id).unwrap_or(false))
.filter(|user_id| self.is_joined(&user_id, &pdu.room_id).unwrap_or(false))
{ {
// Don't notify the user of their own events // Don't notify the user of their own events
if user == pdu.sender { if user == pdu.sender {

View File

@ -49,7 +49,7 @@ impl Users {
} }
/// Create a new user account on this homeserver. /// Create a new user account on this homeserver.
pub fn create(&self, user_id: &UserId, password: &str) -> Result<()> { pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
self.set_password(user_id, password)?; self.set_password(user_id, password)?;
Ok(()) Ok(())
} }
@ -110,15 +110,20 @@ impl Users {
} }
/// Hash and set the user's password to the Argon2 hash /// Hash and set the user's password to the Argon2 hash
pub fn set_password(&self, user_id: &UserId, password: &str) -> Result<()> { pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
if let Ok(hash) = utils::calculate_hash(&password) { if let Some(password) = password {
self.userid_password.insert(user_id.to_string(), &*hash)?; if let Ok(hash) = utils::calculate_hash(&password) {
Ok(()) self.userid_password.insert(user_id.to_string(), &*hash)?;
Ok(())
} else {
Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Password does not meet the requirements.",
))
}
} else { } else {
Err(Error::BadRequest( self.userid_password.insert(user_id.to_string(), "")?;
ErrorKind::InvalidParam, Ok(())
"Password does not meet the requirements.",
))
} }
} }