2018-06-10 11:13:07 +00:00
use activitypub ::{
2018-07-26 20:23:53 +00:00
Activity , Actor , Object , Endpoint , CustomObject ,
2018-06-21 15:14:26 +00:00
actor ::Person ,
2018-09-03 12:48:34 +00:00
collection ::OrderedCollection ,
object ::Image ,
2018-05-16 18:20:44 +00:00
} ;
2018-04-24 09:21:39 +00:00
use bcrypt ;
2018-09-03 18:53:20 +00:00
use chrono ::{ NaiveDateTime , Utc } ;
2018-05-19 07:39:59 +00:00
use diesel ::{ self , QueryDsl , RunQueryDsl , ExpressionMethods , BelongingToDsl , PgConnection , dsl ::any } ;
use openssl ::{
hash ::MessageDigest ,
pkey ::{ PKey , Private } ,
rsa ::Rsa ,
sign
} ;
2018-06-23 16:36:11 +00:00
use plume_common ::activity_pub ::{
2018-07-18 14:58:28 +00:00
ap_accept_header , ActivityStream , Id , IntoId , ApSignature , PublicKey ,
2018-06-23 16:36:11 +00:00
inbox ::WithInbox ,
sign ::{ Signer , gen_keypair }
} ;
2018-05-19 07:39:59 +00:00
use reqwest ::{
Client ,
header ::{ Accept , qitem } ,
mime ::Mime
} ;
use rocket ::{
request ::{ self , FromRequest , Request } ,
outcome ::IntoOutcome
} ;
2018-05-01 11:48:19 +00:00
use serde_json ;
2018-05-01 18:02:29 +00:00
use url ::Url ;
2018-06-18 21:50:40 +00:00
use webfinger ::* ;
2018-04-24 09:21:39 +00:00
2018-06-26 14:21:58 +00:00
use { BASE_URL , USE_HTTPS , ap_url } ;
2018-04-23 09:52:44 +00:00
use db_conn ::DbConn ;
2018-06-23 16:36:11 +00:00
use blogs ::Blog ;
use blog_authors ::BlogAuthor ;
use follows ::Follow ;
use instance ::* ;
use likes ::Like ;
2018-09-03 11:17:59 +00:00
use medias ::Media ;
2018-06-23 16:36:11 +00:00
use post_authors ::PostAuthor ;
use posts ::Post ;
use reshares ::Reshare ;
2018-06-11 14:05:18 +00:00
use safe_string ::SafeString ;
2018-06-23 16:36:11 +00:00
use schema ::users ;
2018-04-23 09:52:44 +00:00
pub const AUTH_COOKIE : & 'static str = " user_id " ;
2018-04-22 18:13:12 +00:00
2018-06-21 20:30:56 +00:00
pub type CustomPerson = CustomObject < ApSignature , Person > ;
2018-06-20 21:51:47 +00:00
#[ derive(Queryable, Identifiable, Serialize, Deserialize, Clone, Debug) ]
2018-04-22 18:13:12 +00:00
pub struct User {
pub id : i32 ,
pub username : String ,
pub display_name : String ,
pub outbox_url : String ,
pub inbox_url : String ,
pub is_admin : bool ,
2018-06-11 14:05:18 +00:00
pub summary : SafeString ,
2018-04-22 18:13:12 +00:00
pub email : Option < String > ,
pub hashed_password : Option < String > ,
2018-04-30 17:46:27 +00:00
pub instance_id : i32 ,
2018-05-01 18:02:29 +00:00
pub creation_date : NaiveDateTime ,
2018-05-03 17:12:01 +00:00
pub ap_url : String ,
pub private_key : Option < String > ,
2018-05-13 18:12:27 +00:00
pub public_key : String ,
2018-07-27 10:53:21 +00:00
pub shared_inbox_url : Option < String > ,
2018-09-03 11:17:59 +00:00
pub followers_endpoint : String ,
pub avatar_id : Option < i32 > ,
2018-09-03 18:53:20 +00:00
pub last_fetched_date : NaiveDateTime
2018-04-22 18:13:12 +00:00
}
#[ derive(Insertable) ]
#[ table_name = " users " ]
pub struct NewUser {
pub username : String ,
pub display_name : String ,
pub outbox_url : String ,
pub inbox_url : String ,
pub is_admin : bool ,
2018-06-11 14:05:18 +00:00
pub summary : SafeString ,
2018-04-22 18:13:12 +00:00
pub email : Option < String > ,
pub hashed_password : Option < String > ,
2018-05-01 18:02:29 +00:00
pub instance_id : i32 ,
2018-05-03 17:12:01 +00:00
pub ap_url : String ,
pub private_key : Option < String > ,
2018-05-13 18:12:27 +00:00
pub public_key : String ,
2018-07-27 10:53:21 +00:00
pub shared_inbox_url : Option < String > ,
2018-09-03 11:17:59 +00:00
pub followers_endpoint : String ,
pub avatar_id : Option < i32 > ,
2018-04-22 18:13:12 +00:00
}
2018-06-21 17:42:17 +00:00
const USER_PREFIX : & 'static str = " @ " ;
2018-04-22 18:13:12 +00:00
impl User {
2018-06-18 13:57:38 +00:00
insert! ( users , NewUser ) ;
2018-06-20 08:44:56 +00:00
get! ( users ) ;
find_by! ( users , find_by_email , email as String ) ;
find_by! ( users , find_by_name , username as String , instance_id as i32 ) ;
2018-06-20 19:06:34 +00:00
find_by! ( users , find_by_ap_url , ap_url as String ) ;
2018-06-18 13:57:38 +00:00
2018-09-09 10:25:55 +00:00
pub fn delete ( & self , conn : & PgConnection ) {
diesel ::delete ( self ) . execute ( conn ) . expect ( " Couldn't remove user from DB " ) ;
}
2018-06-21 17:53:57 +00:00
pub fn get_instance ( & self , conn : & PgConnection ) -> Instance {
Instance ::get ( conn , self . instance_id ) . expect ( " Couldn't find instance " )
}
2018-05-13 11:53:58 +00:00
pub fn grant_admin_rights ( & self , conn : & PgConnection ) {
diesel ::update ( self )
. set ( users ::is_admin . eq ( true ) )
. load ::< User > ( conn )
. expect ( " Couldn't grant admin rights " ) ;
}
2018-04-22 18:13:12 +00:00
2018-05-12 15:30:14 +00:00
pub fn update ( & self , conn : & PgConnection , name : String , email : String , summary : String ) -> User {
diesel ::update ( self )
. set ( (
users ::display_name . eq ( name ) ,
users ::email . eq ( email ) ,
users ::summary . eq ( summary ) ,
) ) . load ::< User > ( conn )
. expect ( " Couldn't update user " )
. into_iter ( ) . nth ( 0 ) . unwrap ( )
}
2018-06-10 19:33:42 +00:00
pub fn count_local ( conn : & PgConnection ) -> usize {
users ::table . filter ( users ::instance_id . eq ( Instance ::local_id ( conn ) ) )
. load ::< User > ( conn )
. expect ( " Couldn't load local users " )
. len ( )
}
2018-05-01 11:48:19 +00:00
pub fn find_local ( conn : & PgConnection , username : String ) -> Option < User > {
User ::find_by_name ( conn , username , Instance ::local_id ( conn ) )
}
pub fn find_by_fqn ( conn : & PgConnection , fqn : String ) -> Option < User > {
if fqn . contains ( " @ " ) { // remote user
2018-05-13 11:53:58 +00:00
match Instance ::find_by_domain ( conn , String ::from ( fqn . split ( " @ " ) . last ( ) . unwrap ( ) ) ) {
2018-05-01 11:48:19 +00:00
Some ( instance ) = > {
match User ::find_by_name ( conn , String ::from ( fqn . split ( " @ " ) . nth ( 0 ) . unwrap ( ) ) , instance . id ) {
Some ( u ) = > Some ( u ) ,
None = > User ::fetch_from_webfinger ( conn , fqn )
}
} ,
None = > User ::fetch_from_webfinger ( conn , fqn )
}
} else { // local user
User ::find_local ( conn , fqn )
}
}
fn fetch_from_webfinger ( conn : & PgConnection , acct : String ) -> Option < User > {
2018-06-26 14:16:59 +00:00
match resolve ( acct . clone ( ) , * USE_HTTPS ) {
2018-07-26 19:35:35 +00:00
Ok ( wf ) = > wf . links . into_iter ( ) . find ( | l | l . mime_type = = Some ( String ::from ( " application/activity+json " ) ) ) . and_then ( | l | User ::fetch_from_url ( conn , l . href . expect ( " No href for AP WF link " ) ) ) ,
2018-05-01 11:48:19 +00:00
Err ( details ) = > {
2018-07-26 19:35:35 +00:00
println! ( " WF Error: {:?} " , details ) ;
2018-05-01 11:48:19 +00:00
None
}
}
}
2018-09-03 18:53:20 +00:00
fn fetch ( url : String ) -> Option < CustomPerson > {
2018-05-01 18:02:29 +00:00
let req = Client ::new ( )
. get ( & url [ .. ] )
2018-07-18 14:58:28 +00:00
. header ( Accept ( ap_accept_header ( ) . into_iter ( ) . map ( | h | qitem ( h . parse ::< Mime > ( ) . expect ( " Invalid Content-Type " ) ) ) . collect ( ) ) )
2018-05-01 18:02:29 +00:00
. send ( ) ;
match req {
Ok ( mut res ) = > {
2018-07-26 20:59:41 +00:00
if let Ok ( text ) = & res . text ( ) {
if let Ok ( ap_sign ) = serde_json ::from_str ::< ApSignature > ( text ) {
if let Ok ( mut json ) = serde_json ::from_str ::< CustomPerson > ( text ) {
json . custom_props = ap_sign ; // without this workaround, publicKey is not correctly deserialized
2018-09-03 18:53:20 +00:00
Some ( json )
2018-07-26 20:59:41 +00:00
} else { None }
} else { None }
} else { None }
2018-05-01 18:02:29 +00:00
} ,
2018-06-26 14:16:59 +00:00
Err ( e ) = > {
2018-07-26 19:35:35 +00:00
println! ( " User fetch error: {:?} " , e ) ;
2018-06-26 14:16:59 +00:00
None
}
2018-05-01 18:02:29 +00:00
}
}
2018-09-03 18:53:20 +00:00
pub fn fetch_from_url ( conn : & PgConnection , url : String ) -> Option < User > {
User ::fetch ( url . clone ( ) ) . map ( | json | ( User ::from_activity ( conn , json , Url ::parse ( url . as_ref ( ) ) . unwrap ( ) . host_str ( ) . unwrap ( ) . to_string ( ) ) ) )
}
2018-06-21 20:30:56 +00:00
fn from_activity ( conn : & PgConnection , acct : CustomPerson , inst : String ) -> User {
2018-05-13 11:53:58 +00:00
let instance = match Instance ::find_by_domain ( conn , inst . clone ( ) ) {
2018-05-01 11:48:19 +00:00
Some ( instance ) = > instance ,
None = > {
2018-06-18 13:57:38 +00:00
Instance ::insert ( conn , NewInstance {
name : inst . clone ( ) ,
public_domain : inst . clone ( ) ,
2018-07-27 17:05:36 +00:00
local : false ,
// We don't really care about all the following for remote instances
long_description : String ::new ( ) ,
short_description : String ::new ( ) ,
default_license : String ::new ( ) ,
2018-07-27 20:16:17 +00:00
open_registrations : true ,
short_description_html : String ::new ( ) ,
long_description_html : String ::new ( )
2018-06-18 13:57:38 +00:00
} )
2018-05-01 11:48:19 +00:00
}
} ;
2018-09-03 11:17:59 +00:00
let avatar = Media ::save_remote ( conn , acct . object . object_props . icon_image ( ) . expect ( " User::from_activity: icon error " )
. object_props . url_string ( ) . expect ( " User::from_activity: icon.url error " ) ) ;
let user = User ::insert ( conn , NewUser {
2018-06-21 20:30:56 +00:00
username : acct . object . ap_actor_props . preferred_username_string ( ) . expect ( " User::from_activity: preferredUsername error " ) ,
display_name : acct . object . object_props . name_string ( ) . expect ( " User::from_activity: name error " ) ,
outbox_url : acct . object . ap_actor_props . outbox_string ( ) . expect ( " User::from_activity: outbox error " ) ,
inbox_url : acct . object . ap_actor_props . inbox_string ( ) . expect ( " User::from_activity: inbox error " ) ,
2018-05-01 11:48:19 +00:00
is_admin : false ,
2018-07-19 08:41:37 +00:00
summary : SafeString ::new ( & acct . object . object_props . summary_string ( ) . unwrap_or ( String ::new ( ) ) ) ,
2018-05-01 11:48:19 +00:00
email : None ,
hashed_password : None ,
2018-05-01 18:02:29 +00:00
instance_id : instance . id ,
2018-06-21 20:30:56 +00:00
ap_url : acct . object . object_props . id_string ( ) . expect ( " User::from_activity: id error " ) ,
public_key : acct . custom_props . public_key_publickey ( ) . expect ( " User::from_activity: publicKey error " )
. public_key_pem_string ( ) . expect ( " User::from_activity: publicKey.publicKeyPem error " ) ,
2018-05-13 18:12:27 +00:00
private_key : None ,
2018-06-21 20:30:56 +00:00
shared_inbox_url : acct . object . ap_actor_props . endpoints_endpoint ( )
2018-07-27 10:53:21 +00:00
. and_then ( | e | e . shared_inbox_string ( ) ) . ok ( ) ,
2018-09-03 11:17:59 +00:00
followers_endpoint : acct . object . ap_actor_props . followers_string ( ) . expect ( " User::from_activity: followers error " ) ,
avatar_id : Some ( avatar . id )
} ) ;
avatar . set_owner ( conn , user . id ) ;
user
2018-05-01 11:48:19 +00:00
}
2018-09-03 18:53:20 +00:00
pub fn refetch ( & self , conn : & PgConnection ) {
User ::fetch ( self . ap_url . clone ( ) ) . map ( | json | {
let avatar = Media ::save_remote ( conn , json . object . object_props . icon_image ( ) . expect ( " User::refetch: icon error " )
. object_props . url_string ( ) . expect ( " User::refetch: icon.url error " ) ) ;
diesel ::update ( self )
. set ( (
users ::username . eq ( json . object . ap_actor_props . preferred_username_string ( ) . expect ( " User::refetch: preferredUsername error " ) ) ,
users ::display_name . eq ( json . object . object_props . name_string ( ) . expect ( " User::refetch: name error " ) ) ,
users ::outbox_url . eq ( json . object . ap_actor_props . outbox_string ( ) . expect ( " User::refetch: outbox error " ) ) ,
users ::inbox_url . eq ( json . object . ap_actor_props . inbox_string ( ) . expect ( " User::refetch: inbox error " ) ) ,
users ::summary . eq ( SafeString ::new ( & json . object . object_props . summary_string ( ) . unwrap_or ( String ::new ( ) ) ) ) ,
users ::followers_endpoint . eq ( json . object . ap_actor_props . followers_string ( ) . expect ( " User::refetch: followers error " ) ) ,
users ::avatar_id . eq ( Some ( avatar . id ) ) ,
users ::last_fetched_date . eq ( Utc ::now ( ) . naive_utc ( ) )
) ) . execute ( conn )
. expect ( " Couldn't update user " )
} ) ;
}
2018-04-23 09:52:44 +00:00
pub fn hash_pass ( pass : String ) -> String {
2018-09-03 16:51:32 +00:00
bcrypt ::hash ( pass . as_str ( ) , 10 ) . unwrap ( )
2018-04-23 09:52:44 +00:00
}
pub fn auth ( & self , pass : String ) -> bool {
2018-06-25 13:38:39 +00:00
if let Ok ( valid ) = bcrypt ::verify ( pass . as_str ( ) , self . hashed_password . clone ( ) . unwrap ( ) . as_str ( ) ) {
valid
} else {
false
}
2018-04-23 09:52:44 +00:00
}
2018-04-23 13:12:59 +00:00
pub fn update_boxes ( & self , conn : & PgConnection ) {
2018-06-21 17:42:17 +00:00
let instance = self . get_instance ( conn ) ;
2018-04-23 13:12:59 +00:00
if self . outbox_url . len ( ) = = 0 {
diesel ::update ( self )
2018-06-21 17:42:17 +00:00
. set ( users ::outbox_url . eq ( instance . compute_box ( USER_PREFIX , self . username . clone ( ) , " outbox " ) ) )
. get_result ::< User > ( conn ) . expect ( " Couldn't update outbox URL " ) ;
2018-04-23 13:12:59 +00:00
}
if self . inbox_url . len ( ) = = 0 {
diesel ::update ( self )
2018-06-21 17:42:17 +00:00
. set ( users ::inbox_url . eq ( instance . compute_box ( USER_PREFIX , self . username . clone ( ) , " inbox " ) ) )
2018-09-03 11:17:59 +00:00
. get_result ::< User > ( conn ) . expect ( " Couldn't update inbox URL " ) ;
2018-05-01 18:02:29 +00:00
}
if self . ap_url . len ( ) = = 0 {
diesel ::update ( self )
2018-06-21 17:42:17 +00:00
. set ( users ::ap_url . eq ( instance . compute_box ( USER_PREFIX , self . username . clone ( ) , " " ) ) )
2018-05-01 18:02:29 +00:00
. get_result ::< User > ( conn ) . expect ( " Couldn't update AP URL " ) ;
2018-04-23 13:12:59 +00:00
}
2018-05-13 18:12:27 +00:00
if self . shared_inbox_url . is_none ( ) {
diesel ::update ( self )
. set ( users ::shared_inbox_url . eq ( ap_url ( format! ( " {} /inbox " , Instance ::get_local ( conn ) . unwrap ( ) . public_domain ) ) ) )
. get_result ::< User > ( conn ) . expect ( " Couldn't update shared inbox URL " ) ;
}
2018-07-27 10:53:21 +00:00
if self . followers_endpoint . len ( ) = = 0 {
diesel ::update ( self )
. set ( users ::followers_endpoint . eq ( instance . compute_box ( USER_PREFIX , self . username . clone ( ) , " followers " ) ) )
. get_result ::< User > ( conn ) . expect ( " Couldn't update followers endpoint " ) ;
}
2018-04-23 13:12:59 +00:00
}
2018-04-29 18:01:42 +00:00
2018-09-09 10:25:55 +00:00
pub fn get_local_page ( conn : & PgConnection , ( min , max ) : ( i32 , i32 ) ) -> Vec < User > {
users ::table . filter ( users ::instance_id . eq ( 1 ) )
. order ( users ::username . asc ( ) )
. offset ( min . into ( ) )
. limit ( ( max - min ) . into ( ) )
. load ::< User > ( conn )
. expect ( " Error getting local users page " )
}
2018-05-16 18:20:44 +00:00
pub fn outbox ( & self , conn : & PgConnection ) -> ActivityStream < OrderedCollection > {
2018-05-18 22:04:30 +00:00
let acts = self . get_activities ( conn ) ;
let n_acts = acts . len ( ) ;
let mut coll = OrderedCollection ::default ( ) ;
coll . collection_props . items = serde_json ::to_value ( acts ) . unwrap ( ) ;
coll . collection_props . set_total_items_u64 ( n_acts as u64 ) . unwrap ( ) ;
2018-05-16 18:20:44 +00:00
ActivityStream ::new ( coll )
2018-04-29 18:01:42 +00:00
}
2018-07-26 20:23:53 +00:00
pub fn fetch_outbox < T : Activity > ( & self ) -> Vec < T > {
let req = Client ::new ( )
. get ( & self . outbox_url [ .. ] )
. header ( Accept ( ap_accept_header ( ) . into_iter ( ) . map ( | h | qitem ( h . parse ::< Mime > ( ) . expect ( " Invalid Content-Type " ) ) ) . collect ( ) ) )
. send ( ) ;
match req {
Ok ( mut res ) = > {
let text = & res . text ( ) . unwrap ( ) ;
let json : serde_json ::Value = serde_json ::from_str ( text ) . unwrap ( ) ;
json [ " items " ] . as_array ( )
. expect ( " Outbox.items is not an array " )
. into_iter ( )
. filter_map ( | j | serde_json ::from_value ( j . clone ( ) ) . ok ( ) )
. collect ::< Vec < T > > ( )
} ,
Err ( e ) = > {
println! ( " User outbox fetch error: {:?} " , e ) ;
vec! [ ]
}
}
}
2018-07-27 10:53:21 +00:00
pub fn fetch_followers_ids ( & self ) -> Vec < String > {
let req = Client ::new ( )
. get ( & self . followers_endpoint [ .. ] )
. header ( Accept ( ap_accept_header ( ) . into_iter ( ) . map ( | h | qitem ( h . parse ::< Mime > ( ) . expect ( " Invalid Content-Type " ) ) ) . collect ( ) ) )
. send ( ) ;
match req {
Ok ( mut res ) = > {
let text = & res . text ( ) . unwrap ( ) ;
let json : serde_json ::Value = serde_json ::from_str ( text ) . unwrap ( ) ;
json [ " items " ] . as_array ( )
. expect ( " Followers.items is not an array " )
. into_iter ( )
. filter_map ( | j | serde_json ::from_value ( j . clone ( ) ) . ok ( ) )
. collect ::< Vec < String > > ( )
} ,
Err ( e ) = > {
println! ( " User followers fetch error: {:?} " , e ) ;
vec! [ ]
}
}
}
2018-05-16 18:20:44 +00:00
fn get_activities ( & self , conn : & PgConnection ) -> Vec < serde_json ::Value > {
2018-04-29 20:23:44 +00:00
use schema ::posts ;
use schema ::post_authors ;
let posts_by_self = PostAuthor ::belonging_to ( self ) . select ( post_authors ::post_id ) ;
let posts = posts ::table . filter ( posts ::id . eq ( any ( posts_by_self ) ) ) . load ::< Post > ( conn ) . unwrap ( ) ;
2018-05-18 22:04:30 +00:00
posts . into_iter ( ) . map ( | p | {
serde_json ::to_value ( p . create_activity ( conn ) ) . unwrap ( )
2018-05-16 18:20:44 +00:00
} ) . collect ::< Vec < serde_json ::Value > > ( )
2018-04-29 18:01:42 +00:00
}
2018-05-01 13:23:23 +00:00
2018-05-13 11:53:58 +00:00
pub fn get_fqn ( & self , conn : & PgConnection ) -> String {
2018-05-13 17:19:23 +00:00
if self . instance_id = = Instance ::local_id ( conn ) {
self . username . clone ( )
} else {
format! ( " {} @ {} " , self . username , self . get_instance ( conn ) . public_domain )
}
2018-05-13 11:53:58 +00:00
}
2018-05-01 13:23:23 +00:00
pub fn get_followers ( & self , conn : & PgConnection ) -> Vec < User > {
use schema ::follows ;
let follows = Follow ::belonging_to ( self ) . select ( follows ::follower_id ) ;
users ::table . filter ( users ::id . eq ( any ( follows ) ) ) . load ::< User > ( conn ) . unwrap ( )
}
2018-07-25 13:50:29 +00:00
pub fn get_followers_page ( & self , conn : & PgConnection , ( min , max ) : ( i32 , i32 ) ) -> Vec < User > {
use schema ::follows ;
let follows = Follow ::belonging_to ( self ) . select ( follows ::follower_id ) ;
users ::table . filter ( users ::id . eq ( any ( follows ) ) )
. offset ( min . into ( ) )
. limit ( ( max - min ) . into ( ) )
. load ::< User > ( conn ) . unwrap ( )
}
2018-05-01 13:23:23 +00:00
pub fn get_following ( & self , conn : & PgConnection ) -> Vec < User > {
use schema ::follows ;
let follows = follows ::table . filter ( follows ::follower_id . eq ( self . id ) ) . select ( follows ::following_id ) ;
users ::table . filter ( users ::id . eq ( any ( follows ) ) ) . load ::< User > ( conn ) . unwrap ( )
}
2018-05-03 17:12:01 +00:00
2018-07-20 15:51:32 +00:00
pub fn is_followed_by ( & self , conn : & PgConnection , other_id : i32 ) -> bool {
2018-06-13 18:06:14 +00:00
use schema ::follows ;
follows ::table
. filter ( follows ::follower_id . eq ( other_id ) )
. filter ( follows ::following_id . eq ( self . id ) )
. load ::< Follow > ( conn )
. expect ( " Couldn't load follow relationship " )
. len ( ) > 0
}
2018-07-20 15:51:32 +00:00
pub fn is_following ( & self , conn : & PgConnection , other_id : i32 ) -> bool {
use schema ::follows ;
follows ::table
. filter ( follows ::follower_id . eq ( self . id ) )
. filter ( follows ::following_id . eq ( other_id ) )
. load ::< Follow > ( conn )
. expect ( " Couldn't load follow relationship " )
. len ( ) > 0
}
2018-05-12 20:56:57 +00:00
pub fn has_liked ( & self , conn : & PgConnection , post : & Post ) -> bool {
use schema ::likes ;
likes ::table
. filter ( likes ::post_id . eq ( post . id ) )
. filter ( likes ::user_id . eq ( self . id ) )
. load ::< Like > ( conn )
. expect ( " Couldn't load likes " )
. len ( ) > 0
}
2018-05-19 09:51:10 +00:00
pub fn has_reshared ( & self , conn : & PgConnection , post : & Post ) -> bool {
use schema ::reshares ;
reshares ::table
. filter ( reshares ::post_id . eq ( post . id ) )
. filter ( reshares ::user_id . eq ( self . id ) )
. load ::< Reshare > ( conn )
. expect ( " Couldn't load reshares " )
. len ( ) > 0
}
2018-06-10 18:16:25 +00:00
pub fn is_author_in ( & self , conn : & PgConnection , blog : Blog ) -> bool {
use schema ::blog_authors ;
blog_authors ::table . filter ( blog_authors ::author_id . eq ( self . id ) )
. filter ( blog_authors ::blog_id . eq ( blog . id ) )
. load ::< BlogAuthor > ( conn )
. expect ( " Couldn't load blog/author relationship " )
. len ( ) > 0
}
2018-05-03 17:12:01 +00:00
pub fn get_keypair ( & self ) -> PKey < Private > {
PKey ::from_rsa ( Rsa ::private_key_from_pem ( self . private_key . clone ( ) . unwrap ( ) . as_ref ( ) ) . unwrap ( ) ) . unwrap ( )
}
2018-05-18 22:04:30 +00:00
2018-09-03 12:48:34 +00:00
pub fn into_activity ( & self , conn : & PgConnection ) -> CustomPerson {
2018-05-18 22:04:30 +00:00
let mut actor = Person ::default ( ) ;
2018-06-21 15:14:26 +00:00
actor . object_props . set_id_string ( self . ap_url . clone ( ) ) . expect ( " User::into_activity: id error " ) ;
2018-06-21 17:23:01 +00:00
actor . object_props . set_name_string ( self . display_name . clone ( ) ) . expect ( " User::into_activity: name error " ) ;
actor . object_props . set_summary_string ( self . summary . get ( ) . clone ( ) ) . expect ( " User::into_activity: summary error " ) ;
2018-06-21 15:14:26 +00:00
actor . object_props . set_url_string ( self . ap_url . clone ( ) ) . expect ( " User::into_activity: url error " ) ;
actor . ap_actor_props . set_inbox_string ( self . inbox_url . clone ( ) ) . expect ( " User::into_activity: inbox error " ) ;
actor . ap_actor_props . set_outbox_string ( self . outbox_url . clone ( ) ) . expect ( " User::into_activity: outbox error " ) ;
2018-06-21 17:53:57 +00:00
actor . ap_actor_props . set_preferred_username_string ( self . username . clone ( ) ) . expect ( " User::into_activity: preferredUsername error " ) ;
2018-07-27 10:53:21 +00:00
actor . ap_actor_props . set_followers_string ( self . followers_endpoint . clone ( ) ) . expect ( " User::into_activity: followers error " ) ;
2018-06-21 15:14:26 +00:00
let mut endpoints = Endpoint ::default ( ) ;
endpoints . set_shared_inbox_string ( ap_url ( format! ( " {} /inbox/ " , BASE_URL . as_str ( ) ) ) ) . expect ( " User::into_activity: endpoints.sharedInbox error " ) ;
actor . ap_actor_props . set_endpoints_endpoint ( endpoints ) . expect ( " User::into_activity: endpoints error " ) ;
2018-06-21 21:12:24 +00:00
let mut public_key = PublicKey ::default ( ) ;
2018-06-22 15:17:53 +00:00
public_key . set_id_string ( format! ( " {} #main-key " , self . ap_url ) ) . expect ( " User::into_activity: publicKey.id error " ) ;
public_key . set_owner_string ( self . ap_url . clone ( ) ) . expect ( " User::into_activity: publicKey.owner error " ) ;
public_key . set_public_key_pem_string ( self . public_key . clone ( ) ) . expect ( " User::into_activity: publicKey.publicKeyPem error " ) ;
2018-06-21 21:12:24 +00:00
let mut ap_signature = ApSignature ::default ( ) ;
2018-06-22 15:17:53 +00:00
ap_signature . set_public_key_publickey ( public_key ) . expect ( " User::into_activity: publicKey error " ) ;
2018-06-21 21:12:24 +00:00
2018-09-03 12:48:34 +00:00
let mut avatar = Image ::default ( ) ;
avatar . object_props . set_url_string ( self . avatar_id . and_then ( | id | Media ::get ( conn , id ) . map ( | m | m . url ( conn ) ) ) . unwrap_or ( String ::new ( ) ) )
. expect ( " User::into_activity: icon.url error " ) ;
actor . object_props . set_icon_object ( avatar ) . expect ( " User::into_activity: icon error " ) ;
2018-06-21 21:12:24 +00:00
CustomPerson ::new ( actor , ap_signature )
2018-05-18 22:04:30 +00:00
}
2018-06-18 16:34:29 +00:00
pub fn to_json ( & self , conn : & PgConnection ) -> serde_json ::Value {
let mut json = serde_json ::to_value ( self ) . unwrap ( ) ;
json [ " fqn " ] = serde_json ::Value ::String ( self . get_fqn ( conn ) ) ;
2018-07-26 14:36:19 +00:00
json [ " name " ] = if self . display_name . len ( ) > 0 {
json! ( self . display_name )
} else {
json! ( self . get_fqn ( conn ) )
} ;
2018-09-03 11:17:59 +00:00
json [ " avatar " ] = json! ( self . avatar_id . and_then ( | id | Media ::get ( conn , id ) . map ( | m | m . url ( conn ) ) ) . unwrap_or ( " /static/default-avatar.png " . to_string ( ) ) ) ;
2018-06-18 16:34:29 +00:00
json
}
2018-06-18 21:50:40 +00:00
pub fn webfinger ( & self , conn : & PgConnection ) -> Webfinger {
Webfinger {
subject : format ! ( " acct:{}@{} " , self . username , self . get_instance ( conn ) . public_domain ) ,
2018-06-21 14:48:54 +00:00
aliases : vec ! [ self . ap_url . clone ( ) ] ,
2018-06-18 21:50:40 +00:00
links : vec ! [
Link {
rel : String ::from ( " http://webfinger.net/rel/profile-page " ) ,
mime_type : None ,
2018-07-26 19:35:35 +00:00
href : Some ( self . ap_url . clone ( ) ) ,
template : None
2018-06-18 21:50:40 +00:00
} ,
Link {
rel : String ::from ( " http://schemas.google.com/g/2010#updates-from " ) ,
mime_type : Some ( String ::from ( " application/atom+xml " ) ) ,
2018-07-26 19:35:35 +00:00
href : Some ( self . get_instance ( conn ) . compute_box ( USER_PREFIX , self . username . clone ( ) , " feed.atom " ) ) ,
template : None
2018-06-18 21:50:40 +00:00
} ,
Link {
rel : String ::from ( " self " ) ,
mime_type : Some ( String ::from ( " application/activity+json " ) ) ,
2018-07-26 19:35:35 +00:00
href : Some ( self . ap_url . clone ( ) ) ,
template : None
2018-06-18 21:50:40 +00:00
}
]
}
}
2018-06-21 17:23:01 +00:00
pub fn from_url ( conn : & PgConnection , url : String ) -> Option < User > {
User ::find_by_ap_url ( conn , url . clone ( ) ) . or_else ( | | {
// The requested user was not in the DB
// We try to fetch it if it is remote
if Url ::parse ( url . as_ref ( ) ) . unwrap ( ) . host_str ( ) . unwrap ( ) ! = BASE_URL . as_str ( ) {
2018-06-23 11:14:03 +00:00
User ::fetch_from_url ( conn , url )
2018-06-21 17:23:01 +00:00
} else {
None
}
} )
}
2018-09-03 12:04:17 +00:00
pub fn set_avatar ( & self , conn : & PgConnection , id : i32 ) {
diesel ::update ( self )
. set ( users ::avatar_id . eq ( id ) )
. execute ( conn )
. expect ( " Couldn't update user avatar " ) ;
}
2018-09-03 18:53:20 +00:00
pub fn needs_update ( & self ) -> bool {
( Utc ::now ( ) . naive_utc ( ) - self . last_fetched_date ) . num_days ( ) > 1
}
2018-05-18 22:04:30 +00:00
}
2018-04-23 09:52:44 +00:00
impl < ' a , ' r > FromRequest < ' a , ' r > for User {
type Error = ( ) ;
fn from_request ( request : & ' a Request < ' r > ) -> request ::Outcome < User , ( ) > {
let conn = request . guard ::< DbConn > ( ) ? ;
request . cookies ( )
. get_private ( AUTH_COOKIE )
. and_then ( | cookie | cookie . value ( ) . parse ( ) . ok ( ) )
. map ( | id | User ::get ( & * conn , id ) . unwrap ( ) )
. or_forward ( ( ) )
}
2018-04-22 18:13:12 +00:00
}
2018-04-23 12:01:32 +00:00
2018-05-18 08:04:40 +00:00
impl IntoId for User {
2018-05-18 22:04:30 +00:00
fn into_id ( self ) -> Id {
2018-05-18 08:04:40 +00:00
Id ::new ( self . ap_url . clone ( ) )
}
}
impl Object for User { }
impl Actor for User { }
impl WithInbox for User {
fn get_inbox_url ( & self ) -> String {
self . inbox_url . clone ( )
}
fn get_shared_inbox_url ( & self ) -> Option < String > {
self . shared_inbox_url . clone ( )
}
2018-07-18 13:49:13 +00:00
fn is_local ( & self ) -> bool {
2018-09-04 13:02:01 +00:00
self . instance_id = = 1
2018-07-18 13:49:13 +00:00
}
2018-05-18 08:04:40 +00:00
}
2018-05-03 19:11:04 +00:00
impl Signer for User {
2018-06-21 15:25:32 +00:00
fn get_key_id ( & self ) -> String {
2018-06-21 14:48:54 +00:00
format! ( " {} #main-key " , self . ap_url )
2018-05-03 17:12:01 +00:00
}
fn sign ( & self , to_sign : String ) -> Vec < u8 > {
let key = self . get_keypair ( ) ;
2018-05-03 19:11:04 +00:00
let mut signer = sign ::Signer ::new ( MessageDigest ::sha256 ( ) , & key ) . unwrap ( ) ;
2018-05-03 17:12:01 +00:00
signer . update ( to_sign . as_bytes ( ) ) . unwrap ( ) ;
signer . sign_to_vec ( ) . unwrap ( )
}
}
2018-04-23 13:12:59 +00:00
impl NewUser {
/// Creates a new local user
pub fn new_local (
2018-06-19 17:29:34 +00:00
conn : & PgConnection ,
2018-04-23 13:12:59 +00:00
username : String ,
display_name : String ,
is_admin : bool ,
summary : String ,
email : String ,
2018-06-19 17:29:34 +00:00
password : String
) -> User {
2018-05-03 19:11:04 +00:00
let ( pub_key , priv_key ) = gen_keypair ( ) ;
2018-06-19 17:29:34 +00:00
User ::insert ( conn , NewUser {
2018-04-23 13:12:59 +00:00
username : username ,
display_name : display_name ,
outbox_url : String ::from ( " " ) ,
inbox_url : String ::from ( " " ) ,
is_admin : is_admin ,
2018-06-11 14:05:18 +00:00
summary : SafeString ::new ( & summary ) ,
2018-04-23 13:12:59 +00:00
email : Some ( email ) ,
hashed_password : Some ( password ) ,
2018-06-19 17:29:34 +00:00
instance_id : Instance ::local_id ( conn ) ,
2018-05-03 17:12:01 +00:00
ap_url : String ::from ( " " ) ,
public_key : String ::from_utf8 ( pub_key ) . unwrap ( ) ,
2018-05-13 18:12:27 +00:00
private_key : Some ( String ::from_utf8 ( priv_key ) . unwrap ( ) ) ,
2018-07-27 10:53:21 +00:00
shared_inbox_url : None ,
2018-09-03 11:17:59 +00:00
followers_endpoint : String ::from ( " " ) ,
avatar_id : None
2018-06-19 17:29:34 +00:00
} )
2018-04-23 13:12:59 +00:00
}
}