Skip to content

Commit

Permalink
Create a virtual user even if the the user already has a user
Browse files Browse the repository at this point in the history
Because only virtual users can be impersonated by the application service
  • Loading branch information
exul committed Mar 16, 2017
1 parent f5a118d commit 557c5b4
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 139 deletions.
1 change: 0 additions & 1 deletion migrations/20161204155847_create_users/up.sql
Expand Up @@ -2,7 +2,6 @@ CREATE TABLE users (
matrix_user_id VARCHAR NOT NULL,
display_name VARCHAR NOT NULL,
language VARCHAR NOT NULL,
is_virtual_user BOOLEAN NOT NULL,
last_message_sent BIG INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Expand Down
@@ -1,10 +1,10 @@
CREATE TABLE users_on_rocketchat_servers (
is_virtual_user BOOLEAN NOT NULL,
matrix_user_id VARCHAR NOT NULL,
rocketchat_server_id INTEGER NOT NULL,
rocketchat_user_id VARCHAR,
rocketchat_auth_token VARCHAR,
rocketchat_username VARCHAR,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (rocketchat_user_id)
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
6 changes: 3 additions & 3 deletions src/matrix-rocketchat/api/matrix/r0.rs
Expand Up @@ -127,8 +127,8 @@ impl super::MatrixApi for MatrixApi {

debug!(self.logger,
"User {} successfully invited into room {}",
matrix_room_id,
matrix_user_id);
matrix_user_id,
matrix_room_id);
Ok(())
}

Expand Down Expand Up @@ -169,7 +169,7 @@ impl super::MatrixApi for MatrixApi {
let body_params = register::BodyParams {
bind_email: None,
password: None,
username: Some(user_id_local_part),
username: Some(user_id_local_part.to_lowercase()),
device_id: None,
initial_device_display_name: None,
auth: None,
Expand Down
6 changes: 3 additions & 3 deletions src/matrix-rocketchat/api/rest_api.rs
Expand Up @@ -70,9 +70,9 @@ impl RestApi {
}

fn encode_url(base: String, parameters: &HashMap<&str, &str>) -> Result<String> {
let query_string = parameters.iter()
.fold("?".to_string(),
|init, (k, v)| [init, [k.to_string(), v.to_string()].join("=")].join("&"));
let query_string = parameters.iter().fold("?".to_string(), |init, (k, v)| {
[init, [k.to_string(), v.to_string()].join("=")].join("&")
});
let url_string = [base, query_string].join("");
let url = Url::parse(&url_string).chain_err(|| ErrorKind::ApiCallFailed(url_string))?;
Ok(format!("{}", url))
Expand Down
6 changes: 3 additions & 3 deletions src/matrix-rocketchat/api/rocketchat/v1.rs
Expand Up @@ -241,9 +241,9 @@ fn build_error(endpoint: String, body: &str, status_code: &StatusCode) -> Error

if *status_code == StatusCode::Unauthorized {
return Error {
error_chain: ErrorKind::AuthenticationFailed(rocketchat_error_resp.message).into(),
user_message: Some(t!(["errors", "authentication_failed"])),
};
error_chain: ErrorKind::AuthenticationFailed(rocketchat_error_resp.message).into(),
user_message: Some(t!(["errors", "authentication_failed"])),
};
}

Error::from(ErrorKind::RocketchatError(rocketchat_error_resp.message))
Expand Down
2 changes: 1 addition & 1 deletion src/matrix-rocketchat/db/schema.rs
Expand Up @@ -5,7 +5,6 @@ table! {
matrix_user_id -> Text,
display_name -> Text,
language -> Text,
is_virtual_user -> Bool,
last_message_sent -> BigInt,
created_at -> Timestamp,
updated_at -> Timestamp,
Expand Down Expand Up @@ -46,6 +45,7 @@ table! {

table! {
users_on_rocketchat_servers (matrix_user_id, rocketchat_server_id) {
is_virtual_user -> Bool,
matrix_user_id -> Text,
rocketchat_server_id -> Integer,
rocketchat_user_id -> Nullable<Text>,
Expand Down
18 changes: 10 additions & 8 deletions src/matrix-rocketchat/db/user.rs
Expand Up @@ -16,8 +16,6 @@ pub struct User {
pub display_name: String,
/// The language the user prefers to get messages in.
pub language: String,
/// Flag to indicate if the user is only used to send messages from Rocket.Chat
pub is_virtual_user: bool,
/// Time when the user sent the last message in seconds since UNIX_EPOCH
pub last_message_sent: i64,
/// created timestamp
Expand All @@ -36,20 +34,23 @@ pub struct NewUser<'a> {
pub display_name: String,
/// The language the user prefers to get messages in.
pub language: &'a str,
/// Flag to indicate if the user is only used to send messages from Rocket.Chat
pub is_virtual_user: bool,
}

impl User {
/// Insert a new `User` into the database.
pub fn insert(connection: &SqliteConnection, user: &NewUser) -> Result<User> {
diesel::insert(user).into(users::table).execute(connection).chain_err(|| ErrorKind::DBInsertError)?;
diesel::insert(user).into(users::table)
.execute(connection)
.chain_err(|| ErrorKind::DBInsertError)?;
User::find(connection, &user.matrix_user_id)
}

/// Find a `User` by his matrix user ID, return an error if the user is not found
pub fn find(connection: &SqliteConnection, matrix_user_id: &UserId) -> Result<User> {
users::table.find(matrix_user_id).first(connection).chain_err(|| ErrorKind::DBSelectError).map_err(Error::from)
users::table.find(matrix_user_id)
.first(connection)
.chain_err(|| ErrorKind::DBSelectError)
.map_err(Error::from)
}

/// Find or create `User` with a given Matrix user ID.
Expand All @@ -61,7 +62,6 @@ impl User {
matrix_user_id: matrix_user_id.clone(),
display_name: matrix_user_id.to_string(),
language: DEFAULT_LANGUAGE,
is_virtual_user: false,
};
User::insert(connection, &new_user)
}
Expand All @@ -70,7 +70,9 @@ impl User {

/// Find a `User` by his matrix user ID. Returns `None`, if the user is not found.
pub fn find_by_matrix_user_id(connection: &SqliteConnection, matrix_user_id: &UserId) -> Result<Option<User>> {
let users = users::table.find(matrix_user_id).load(connection).chain_err(|| ErrorKind::DBSelectError)?;
let users = users::table.find(matrix_user_id)
.load(connection)
.chain_err(|| ErrorKind::DBSelectError)?;
Ok(users.into_iter().next())
}
}
17 changes: 12 additions & 5 deletions src/matrix-rocketchat/db/user_on_rocketchat_server.rs
Expand Up @@ -10,6 +10,8 @@ use super::User;
/// A user on a Rocket.Chat server.
#[derive(Debug, Queryable)]
pub struct UserOnRocketchatServer {
/// Flag to indicate if the user is only used to send messages from Rocket.Chat
pub is_virtual_user: bool,
/// The users unique id on the Rocket.Chat server.
pub matrix_user_id: UserId,
/// The unique id for the Rocket.Chat server
Expand All @@ -30,6 +32,8 @@ pub struct UserOnRocketchatServer {
#[derive(Insertable)]
#[table_name="users_on_rocketchat_servers"]
pub struct NewUserOnRocketchatServer {
/// Flag to indicate if the user is only used to send messages from Rocket.Chat
pub is_virtual_user: bool,
/// The users unique id on the Rocket.Chat server.
pub matrix_user_id: UserId,
/// The unique id for the Rocket.Chat server
Expand All @@ -47,9 +51,11 @@ impl UserOnRocketchatServer {
pub fn upsert(connection: &SqliteConnection,
user_on_rocketchat_server: &NewUserOnRocketchatServer)
-> Result<UserOnRocketchatServer> {
let users_on_rocketchat_server: Vec<UserOnRocketchatServer> = users_on_rocketchat_servers::table
.find((&user_on_rocketchat_server.matrix_user_id, &user_on_rocketchat_server.rocketchat_server_id))
.load(connection).chain_err(|| ErrorKind::DBSelectError)?;
let users_on_rocketchat_server: Vec<UserOnRocketchatServer> =
users_on_rocketchat_servers::table.find((&user_on_rocketchat_server.matrix_user_id,
&user_on_rocketchat_server.rocketchat_server_id))
.load(connection)
.chain_err(|| ErrorKind::DBSelectError)?;

match users_on_rocketchat_server.into_iter().next() {
Some(existing_user_on_rocketchat_server) => {
Expand Down Expand Up @@ -85,9 +91,10 @@ impl UserOnRocketchatServer {
/// Find a `UserOnRocketchatServer` by his Rocket.Chat user ID. Returns `None`, if the `UserOnRocketchatServer` is not found.
pub fn find_by_rocketchat_user_id(connection: &SqliteConnection,
rocketchat_server_id: i32,
rocketchat_user_id: String)
rocketchat_user_id: String,
is_virtual_user: bool)
-> Result<Option<UserOnRocketchatServer>> {
let users_on_rocketchat_servers = users_on_rocketchat_servers::table.filter(users_on_rocketchat_servers::rocketchat_server_id.eq(rocketchat_server_id).and(users_on_rocketchat_servers::rocketchat_user_id.eq(rocketchat_user_id)))
let users_on_rocketchat_servers = users_on_rocketchat_servers::table.filter(users_on_rocketchat_servers::rocketchat_server_id.eq(rocketchat_server_id).and(users_on_rocketchat_servers::rocketchat_user_id.eq(rocketchat_user_id)).and(users_on_rocketchat_servers::is_virtual_user.eq(is_virtual_user)))
.load(connection)
.chain_err(|| ErrorKind::DBSelectError)?;
Ok(users_on_rocketchat_servers.into_iter().next())
Expand Down
36 changes: 20 additions & 16 deletions src/matrix-rocketchat/handlers/events/command_handler.rs
Expand Up @@ -103,6 +103,7 @@ impl<'a> CommandHandler<'a> {
room.set_rocketchat_server_id(self.connection, rocketchat_server.id)?;

let new_user_on_rocketchat_server = NewUserOnRocketchatServer {
is_virtual_user: false,
matrix_user_id: event.user_id.clone(),
rocketchat_server_id: rocketchat_server.id,
rocketchat_user_id: None,
Expand Down Expand Up @@ -162,8 +163,9 @@ impl<'a> CommandHandler<'a> {
None => t!(["admin_room", "connection_instructions"]).with_vars(vec![("as_url", self.config.as_url.clone())]),
};

self.matrix_api
.send_text_message_event(event.room_id.clone(), self.config.matrix_bot_user_id()?, body.l(&user.language))
self.matrix_api.send_text_message_event(event.room_id.clone(),
self.config.matrix_bot_user_id()?,
body.l(&user.language))
}

fn login(&self, event: &MessageEvent, rocketchat_server: &RocketchatServer, message: &str) -> Result<()> {
Expand All @@ -182,8 +184,8 @@ impl<'a> CommandHandler<'a> {

let (rocketchat_user_id, rocketchat_auth_token) = rocketchat_api.login(username, &password)?;
user_on_rocketchat_server.set_credentials(self.connection,
Some(rocketchat_user_id.clone()),
Some(rocketchat_auth_token.clone()))?;
Some(rocketchat_user_id.clone()),
Some(rocketchat_auth_token.clone()))?;

let username = rocketchat_api.username(rocketchat_user_id, rocketchat_auth_token)?;
user_on_rocketchat_server.set_rocketchat_username(self.connection, Some(username))?;
Expand All @@ -201,8 +203,9 @@ impl<'a> CommandHandler<'a> {
let rocketchat_api = RocketchatApi::new(rocketchat_server.rocketchat_url.clone(),
rocketchat_server.rocketchat_token.clone(),
self.logger.clone())?;
let channels = rocketchat_api.channels_list(user_on_rocketchat_server.rocketchat_user_id.unwrap_or_default(),
user_on_rocketchat_server.rocketchat_auth_token.unwrap_or_default())?;
let channels =
rocketchat_api.channels_list(user_on_rocketchat_server.rocketchat_user_id.unwrap_or_default(),
user_on_rocketchat_server.rocketchat_auth_token.unwrap_or_default())?;

let bot_matrix_user_id = self.config.matrix_bot_user_id()?;
let channels_list = self.build_channels_list(rocketchat_server.id, &event.user_id, channels)?;
Expand All @@ -221,7 +224,9 @@ impl<'a> CommandHandler<'a> {

let channels =
rocketchat_api.channels_list(user_on_rocketchat_server.rocketchat_user_id.clone().unwrap_or_default(),
user_on_rocketchat_server.rocketchat_auth_token.clone().unwrap_or_default())?;
user_on_rocketchat_server.rocketchat_auth_token
.clone()
.unwrap_or_default())?;

let mut command = message.split_whitespace().collect::<Vec<&str>>().into_iter();
let channel_name = command.by_ref().nth(1).unwrap_or_default();
Expand All @@ -242,12 +247,10 @@ impl<'a> CommandHandler<'a> {
}

let username = user_on_rocketchat_server.rocketchat_username.clone().unwrap_or_default();
if !channel.usernames
.iter()
.any(|u| u == &username) {
if !channel.usernames.iter().any(|u| u == &username) {
bail_error!(ErrorKind::RocketchatJoinFirst(channel_name.to_string()),
t!(["errors", "rocketchat_join_first"])
.with_vars(vec![("channel_name", channel_name.to_string())]));
t!(["errors", "rocketchat_join_first"]).with_vars(vec![("channel_name",
channel_name.to_string())]));
}

let room =
Expand All @@ -264,11 +267,12 @@ impl<'a> CommandHandler<'a> {
UserInRoom::insert(self.connection, &new_user_in_room)?;

let bot_matrix_user_id = self.config.matrix_bot_user_id()?;
let message = t!(["admin_room", "room_successfully_bridged"])
.with_vars(vec![("channel_name", channel.name.clone())]);
let message =
t!(["admin_room", "room_successfully_bridged"]).with_vars(vec![("channel_name", channel.name.clone())]);
info!(self.logger, "Successfully bridged room {}", channel.id.clone());
self.matrix_api
.send_text_message_event(event.room_id.clone(), bot_matrix_user_id, message.l(&user.language))
self.matrix_api.send_text_message_event(event.room_id.clone(),
bot_matrix_user_id,
message.l(&user.language))
})
.map_err(Error::from)
}
Expand Down
14 changes: 9 additions & 5 deletions src/matrix-rocketchat/handlers/events/room_handler.rs
Expand Up @@ -106,8 +106,8 @@ impl<'a> RoomHandler<'a> {
fn handle_bot_join(&self, matrix_room_id: RoomId, matrix_bot_user_id: UserId) -> Result<()> {
let room = Room::find(self.connection, &matrix_room_id)?;
let users_in_room = room.users(self.connection)?;
let invitation_submitter = users_in_room.first()
.expect("There is always a user in the room, because this user invited the bot");
let invitation_submitter =
users_in_room.first().expect("There is always a user in the room, because this user invited the bot");

if !self.is_private_room(matrix_room_id.clone())? {
return self.handle_non_private_room(&room, invitation_submitter, matrix_bot_user_id);
Expand Down Expand Up @@ -156,7 +156,9 @@ impl<'a> RoomHandler<'a> {
}

fn is_private_room(&self, matrix_room_id: RoomId) -> Result<bool> {
Ok(self.matrix_api.get_room_members(matrix_room_id)?.len() <= 2)
Ok(self.matrix_api
.get_room_members(matrix_room_id)?
.len() <= 2)
}

fn handle_non_private_room(&self, room: &Room, invitation_submitter: &User, matrix_bot_user_id: UserId) -> Result<()> {
Expand All @@ -179,8 +181,10 @@ impl<'a> RoomHandler<'a> {

fn admin_room_language(&self, room: &Room) -> Result<String> {
let matrix_bot_user_id = self.config.matrix_bot_user_id()?;
let users: Vec<User> =
room.users(self.connection)?.into_iter().filter(|user| user.matrix_user_id != matrix_bot_user_id).collect();
let users: Vec<User> = room.users(self.connection)?
.into_iter()
.filter(|user| user.matrix_user_id != matrix_bot_user_id)
.collect();
let user = users.first().expect("An admin room always contains another user");
Ok(user.language.clone())
}
Expand Down

0 comments on commit 557c5b4

Please sign in to comment.