Merge pull request 'Add displayname and avatar_url endpoints' (#5) from MTRNord/matrixserver:accountdata into master

next
timo 2020-04-09 21:21:28 +02:00 committed by timokoesters
commit 3f2bf208a9
No known key found for this signature in database
GPG Key ID: 24DA7517711A2BA4
3 changed files with 230 additions and 1 deletions

View File

@ -60,6 +60,56 @@ impl Data {
.map(|bytes| utils::string_from_bytes(&bytes)) .map(|bytes| utils::string_from_bytes(&bytes))
} }
/// Removes a displayname.
pub fn displayname_remove(&self, user_id: &UserId) {
self.db
.userid_displayname
.remove(user_id.to_string())
.unwrap();
}
/// Set a new displayname.
pub fn displayname_set(&self, user_id: &UserId, displayname: Option<String>) {
self.db
.userid_displayname
.insert(user_id.to_string(), &*displayname.unwrap_or_default())
.unwrap();
}
/// Get a the displayname of a user.
pub fn displayname_get(&self, user_id: &UserId) -> Option<String> {
self.db
.userid_displayname
.get(user_id.to_string())
.unwrap()
.map(|bytes| utils::string_from_bytes(&bytes))
}
/// Removes a avatar_url.
pub fn avatar_url_remove(&self, user_id: &UserId) {
self.db
.userid_avatarurl
.remove(user_id.to_string())
.unwrap();
}
/// Set a new avatar_url.
pub fn avatar_url_set(&self, user_id: &UserId, avatar_url: String) {
self.db
.userid_avatarurl
.insert(user_id.to_string(), &*avatar_url)
.unwrap();
}
/// Get a the avatar_url of a user.
pub fn avatar_url_get(&self, user_id: &UserId) -> Option<String> {
self.db
.userid_avatarurl
.get(user_id.to_string())
.unwrap()
.map(|bytes| utils::string_from_bytes(&bytes))
}
/// Add a new device to a user. /// Add a new device to a user.
pub fn device_add(&self, user_id: &UserId, device_id: &str) { pub fn device_add(&self, user_id: &UserId, device_id: &str) {
if self if self

View File

@ -52,6 +52,8 @@ impl MultiValue {
pub struct Database { pub struct Database {
pub userid_password: sled::Tree, pub userid_password: sled::Tree,
pub userid_deviceids: MultiValue, pub userid_deviceids: MultiValue,
pub userid_displayname: sled::Tree,
pub userid_avatarurl: sled::Tree,
pub deviceid_token: sled::Tree, pub deviceid_token: sled::Tree,
pub token_userid: sled::Tree, pub token_userid: sled::Tree,
pub pduid_pdus: sled::Tree, pub pduid_pdus: sled::Tree,
@ -75,6 +77,8 @@ impl Database {
Self { Self {
userid_password: db.open_tree("userid_password").unwrap(), userid_password: db.open_tree("userid_password").unwrap(),
userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()), userid_deviceids: MultiValue(db.open_tree("userid_deviceids").unwrap()),
userid_displayname: db.open_tree("userid_displayname").unwrap(),
userid_avatarurl: db.open_tree("userid_avatarurl").unwrap(),
deviceid_token: db.open_tree("deviceid_token").unwrap(), deviceid_token: db.open_tree("deviceid_token").unwrap(),
token_userid: db.open_tree("token_userid").unwrap(), token_userid: db.open_tree("token_userid").unwrap(),
pduid_pdus: db.open_tree("pduid_pdus").unwrap(), pduid_pdus: db.open_tree("pduid_pdus").unwrap(),
@ -103,6 +107,22 @@ impl Database {
String::from_utf8_lossy(&v), String::from_utf8_lossy(&v),
); );
} }
println!("# UserId -> Displayname:");
for (k, v) in self.userid_displayname.iter().map(|r| r.unwrap()) {
println!(
"{:?} -> {:?}",
String::from_utf8_lossy(&k),
String::from_utf8_lossy(&v),
);
}
println!("# UserId -> AvatarURL:");
for (k, v) in self.userid_avatarurl.iter().map(|r| r.unwrap()) {
println!(
"{:?} -> {:?}",
String::from_utf8_lossy(&k),
String::from_utf8_lossy(&v),
);
}
println!("\n# DeviceId -> Token:"); println!("\n# DeviceId -> Token:");
for (k, v) in self.deviceid_token.iter().map(|r| r.unwrap()) { for (k, v) in self.deviceid_token.iter().map(|r| r.unwrap()) {
println!( println!(

View File

@ -1,4 +1,5 @@
#![feature(proc_macro_hygiene, decl_macro)] #![feature(proc_macro_hygiene, decl_macro)]
mod data; mod data;
mod database; mod database;
mod pdu; mod pdu;
@ -26,6 +27,9 @@ use ruma_client_api::{
membership::{join_room_by_id, join_room_by_id_or_alias}, membership::{join_room_by_id, join_room_by_id_or_alias},
message::create_message_event, message::create_message_event,
presence::set_presence, presence::set_presence,
profile::{
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
},
push::get_pushrules_all, push::get_pushrules_all,
room::create_room, room::create_room,
session::{get_login_types, login}, session::{get_login_types, login},
@ -39,7 +43,12 @@ use ruma_events::{collections::only::Event, EventType};
use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId}; use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId};
use ruma_wrapper::{MatrixResult, Ruma}; use ruma_wrapper::{MatrixResult, Ruma};
use serde_json::json; use serde_json::json;
use std::{collections::HashMap, convert::TryInto, path::PathBuf, time::Duration}; use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
path::PathBuf,
time::Duration,
};
const GUEST_NAME_LENGTH: usize = 10; const GUEST_NAME_LENGTH: usize = 10;
const DEVICE_ID_LENGTH: usize = 10; const DEVICE_ID_LENGTH: usize = 10;
@ -282,6 +291,151 @@ fn get_global_account_data_route(
})) }))
} }
#[put("/_matrix/client/r0/profile/<_user_id>/displayname", data = "<body>")]
fn set_displayname_route(
data: State<Data>,
body: Ruma<set_display_name::Request>,
_user_id: String,
) -> MatrixResult<set_display_name::Response> {
let user_id = body.user_id.clone().expect("user is authenticated");
// Send error on None
// Synapse returns a parsing error but the spec doesn't require this
if body.displayname.is_none() {
debug!("Request was missing the displayname payload.");
return MatrixResult(Err(Error {
kind: ErrorKind::MissingParam,
message: "Missing displayname.".to_owned(),
status_code: http::StatusCode::BAD_REQUEST,
}));
}
if let Some(displayname) = &body.displayname {
// Some("") will clear the displayname
if displayname == "" {
data.displayname_remove(&user_id);
} else {
data.displayname_set(&user_id, body.displayname.clone());
// TODO send a new m.room.member join event with the updated displayname
// TODO send a new m.presence event with the updated displayname
}
}
MatrixResult(Ok(set_display_name::Response))
}
#[get(
"/_matrix/client/r0/profile/<user_id_raw>/displayname",
data = "<body>"
)]
fn get_displayname_route(
data: State<Data>,
body: Ruma<get_display_name::Request>,
user_id_raw: String,
) -> MatrixResult<get_display_name::Response> {
let user_id = (*body).user_id.clone();
if !data.user_exists(&user_id) {
// Return 404 if we don't have a profile for this id
debug!("Profile was not found.");
return MatrixResult(Err(Error {
kind: ErrorKind::NotFound,
message: "Profile was not found.".to_owned(),
status_code: http::StatusCode::NOT_FOUND,
}));
}
if let Some(displayname) = data.displayname_get(&user_id) {
return MatrixResult(Ok(get_display_name::Response {
displayname: Some(displayname),
}));
}
// The user has no displayname
MatrixResult(Ok(get_display_name::Response { displayname: None }))
}
#[put("/_matrix/client/r0/profile/<_user_id>/avatar_url", data = "<body>")]
fn set_avatar_url_route(
data: State<Data>,
body: Ruma<set_avatar_url::Request>,
_user_id: String,
) -> MatrixResult<set_avatar_url::Response> {
let user_id = body.user_id.clone().expect("user is authenticated");
if !body.avatar_url.starts_with("mxc://") {
debug!("Request contains an invalid avatar_url.");
return MatrixResult(Err(Error {
kind: ErrorKind::InvalidParam,
message: "avatar_url has to start with mxc://.".to_owned(),
status_code: http::StatusCode::BAD_REQUEST,
}));
}
// TODO in the future when we can handle media uploads make sure that this url is our own server
// TODO also make sure this is valid mxc:// format (not only starting with it)
if body.avatar_url == "" {
data.avatar_url_remove(&user_id);
} else {
data.avatar_url_set(&user_id, body.avatar_url.clone());
// TODO send a new m.room.member join event with the updated avatar_url
// TODO send a new m.presence event with the updated avatar_url
}
MatrixResult(Ok(set_avatar_url::Response))
}
#[get("/_matrix/client/r0/profile/<user_id_raw>/avatar_url", data = "<body>")]
fn get_avatar_url_route(
data: State<Data>,
body: Ruma<get_avatar_url::Request>,
user_id_raw: String,
) -> MatrixResult<get_avatar_url::Response> {
let user_id = (*body).user_id.clone();
if !data.user_exists(&user_id) {
// Return 404 if we don't have a profile for this id
debug!("Profile was not found.");
return MatrixResult(Err(Error {
kind: ErrorKind::NotFound,
message: "Profile was not found.".to_owned(),
status_code: http::StatusCode::NOT_FOUND,
}));
}
if let Some(avatar_url) = data.avatar_url_get(&user_id) {
return MatrixResult(Ok(get_avatar_url::Response {
avatar_url: Some(avatar_url),
}));
}
// The user has no avatar
MatrixResult(Ok(get_avatar_url::Response { avatar_url: None }))
}
#[get("/_matrix/client/r0/profile/<user_id_raw>", data = "<body>")]
fn get_profile_route(
data: State<Data>,
body: Ruma<get_profile::Request>,
user_id_raw: String,
) -> MatrixResult<get_profile::Response> {
let user_id = (*body).user_id.clone();
let avatar_url = data.avatar_url_get(&user_id);
let displayname = data.displayname_get(&user_id);
if avatar_url.is_some() || displayname.is_some() {
return MatrixResult(Ok(get_profile::Response {
avatar_url,
displayname,
}));
}
// Return 404 if we don't have a profile for this id
debug!("Profile was not found.");
MatrixResult(Err(Error {
kind: ErrorKind::NotFound,
message: "Profile was not found.".to_owned(),
status_code: http::StatusCode::NOT_FOUND,
}))
}
#[put("/_matrix/client/r0/presence/<_user_id>/status", data = "<body>")] #[put("/_matrix/client/r0/presence/<_user_id>/status", data = "<body>")]
fn set_presence_route( fn set_presence_route(
body: Ruma<set_presence::Request>, body: Ruma<set_presence::Request>,
@ -634,6 +788,11 @@ fn main() {
create_filter_route, create_filter_route,
set_global_account_data_route, set_global_account_data_route,
get_global_account_data_route, get_global_account_data_route,
set_displayname_route,
get_displayname_route,
set_avatar_url_route,
get_avatar_url_route,
get_profile_route,
set_presence_route, set_presence_route,
get_keys_route, get_keys_route,
upload_keys_route, upload_keys_route,