Merge branch 'account' into 'master'
Fix account and media bugs See merge request famedly/conduit!97next
commit
81715bd84d
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()?;
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue