user.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. use std::collections::HashMap;
  2. use std::sync::Arc;
  3. use anyhow::{anyhow, Error};
  4. use client_api::entity::dto::UserUpdateParams;
  5. use client_api::entity::{
  6. AFUserProfileView, AFWorkspace, AFWorkspaces, InsertCollabParams, OAuthProvider,
  7. };
  8. use collab_define::CollabObject;
  9. use flowy_error::{ErrorCode, FlowyError};
  10. use flowy_user_deps::cloud::UserCloudService;
  11. use flowy_user_deps::entities::*;
  12. use lib_infra::box_any::BoxAny;
  13. use lib_infra::future::FutureResult;
  14. use crate::af_cloud::{AFCloudClient, AFServer};
  15. use crate::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL};
  16. pub(crate) struct AFCloudUserAuthServiceImpl<T> {
  17. server: T,
  18. }
  19. impl<T> AFCloudUserAuthServiceImpl<T> {
  20. pub(crate) fn new(server: T) -> Self {
  21. Self { server }
  22. }
  23. }
  24. impl<T> UserCloudService for AFCloudUserAuthServiceImpl<T>
  25. where
  26. T: AFServer,
  27. {
  28. fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
  29. let try_get_client = self.server.try_get_client();
  30. FutureResult::new(async move {
  31. let params = oauth_params_from_box_any(params)?;
  32. let resp = user_sign_up_request(try_get_client?, params).await?;
  33. Ok(resp)
  34. })
  35. }
  36. // Zack: Not sure if this is needed anymore since sign_up handles both cases
  37. fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
  38. let try_get_client = self.server.try_get_client();
  39. FutureResult::new(async move {
  40. let client = try_get_client?;
  41. let params = oauth_params_from_box_any(params)?;
  42. let resp = user_sign_in_with_url(client, params).await?;
  43. Ok(resp)
  44. })
  45. }
  46. fn sign_out(&self, _token: Option<String>) -> FutureResult<(), Error> {
  47. let try_get_client = self.server.try_get_client();
  48. FutureResult::new(async move { Ok(try_get_client?.sign_out().await?) })
  49. }
  50. fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, Error> {
  51. let email = email.to_string();
  52. let try_get_client = self.server.try_get_client();
  53. FutureResult::new(async move {
  54. // TODO(nathan): replace the admin_email and admin_password with encryption key
  55. let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap();
  56. let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap();
  57. let url = try_get_client?
  58. .generate_sign_in_url_with_email(&admin_email, &admin_password, &email)
  59. .await?;
  60. Ok(url)
  61. })
  62. }
  63. fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, Error> {
  64. let provider = OAuthProvider::from(provider);
  65. let try_get_client = self.server.try_get_client();
  66. FutureResult::new(async move {
  67. let provider = provider.ok_or(anyhow!("invalid provider"))?;
  68. let url = try_get_client?
  69. .generate_oauth_url_with_provider(&provider)
  70. .await?;
  71. Ok(url)
  72. })
  73. }
  74. fn update_user(
  75. &self,
  76. _credential: UserCredentials,
  77. params: UpdateUserProfileParams,
  78. ) -> FutureResult<(), Error> {
  79. let try_get_client = self.server.try_get_client();
  80. FutureResult::new(async move {
  81. let client = try_get_client?;
  82. client
  83. .update(UserUpdateParams {
  84. name: params.name,
  85. email: params.email,
  86. password: params.password,
  87. })
  88. .await?;
  89. Ok(())
  90. })
  91. }
  92. fn get_user_profile(
  93. &self,
  94. _credential: UserCredentials,
  95. ) -> FutureResult<Option<UserProfile>, Error> {
  96. let try_get_client = self.server.try_get_client();
  97. FutureResult::new(async move {
  98. let client = try_get_client?;
  99. let profile = client.profile().await?;
  100. let encryption_type = encryption_type_from_profile(&profile);
  101. Ok(Some(UserProfile {
  102. email: profile.email.unwrap_or("".to_string()),
  103. name: profile.name.unwrap_or("".to_string()),
  104. token: token_from_client(client).await.unwrap_or("".to_string()),
  105. icon_url: "".to_owned(),
  106. openai_key: "".to_owned(),
  107. stability_ai_key: "".to_owned(),
  108. workspace_id: match profile.latest_workspace_id {
  109. Some(w) => w.to_string(),
  110. None => "".to_string(),
  111. },
  112. auth_type: AuthType::AFCloud,
  113. encryption_type,
  114. uid: profile.uid.ok_or(anyhow!("no uid found"))?,
  115. }))
  116. })
  117. }
  118. fn get_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
  119. let try_get_client = self.server.try_get_client();
  120. FutureResult::new(async move {
  121. let workspaces = try_get_client?.workspaces().await?;
  122. Ok(to_user_workspaces(workspaces)?)
  123. })
  124. }
  125. fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error> {
  126. let try_get_client = self.server.try_get_client();
  127. FutureResult::new(async move {
  128. // from params
  129. let token = credential.token.ok_or(anyhow!("expecting token"))?;
  130. let uuid = credential.uuid.ok_or(anyhow!("expecting uuid"))?;
  131. let uid = credential.uid.ok_or(anyhow!("expecting uid"))?;
  132. // from cloud
  133. let client = try_get_client?;
  134. let profile = client.profile().await?;
  135. let client_token = client.access_token()?;
  136. // compare and check
  137. if uuid != profile.uuid.ok_or(anyhow!("expecting uuid"))?.to_string() {
  138. return Err(anyhow!("uuid mismatch"));
  139. }
  140. if uid != profile.uid.ok_or(anyhow!("expecting uid"))? {
  141. return Err(anyhow!("uid mismatch"));
  142. }
  143. if token != client_token {
  144. return Err(anyhow!("token mismatch"));
  145. }
  146. Ok(())
  147. })
  148. }
  149. fn add_workspace_member(
  150. &self,
  151. user_email: String,
  152. workspace_id: String,
  153. ) -> FutureResult<(), Error> {
  154. let try_get_client = self.server.try_get_client();
  155. FutureResult::new(async move {
  156. try_get_client?
  157. .add_workspace_members(workspace_id.parse()?, vec![user_email])
  158. .await?;
  159. Ok(())
  160. })
  161. }
  162. fn remove_workspace_member(
  163. &self,
  164. user_email: String,
  165. workspace_id: String,
  166. ) -> FutureResult<(), Error> {
  167. let try_get_client = self.server.try_get_client();
  168. FutureResult::new(async move {
  169. try_get_client?
  170. .remove_workspace_members(workspace_id.parse()?, vec![user_email])
  171. .await?;
  172. Ok(())
  173. })
  174. }
  175. fn get_user_awareness_updates(&self, _uid: i64) -> FutureResult<Vec<Vec<u8>>, Error> {
  176. // TODO(nathan): implement the RESTful API for this
  177. FutureResult::new(async { Ok(vec![]) })
  178. }
  179. fn reset_workspace(&self, _collab_object: CollabObject) -> FutureResult<(), Error> {
  180. // TODO(nathan): implement the RESTful API for this
  181. FutureResult::new(async { Ok(()) })
  182. }
  183. fn create_collab_object(
  184. &self,
  185. collab_object: &CollabObject,
  186. data: Vec<u8>,
  187. ) -> FutureResult<(), Error> {
  188. let try_get_client = self.server.try_get_client();
  189. let collab_object = collab_object.clone();
  190. FutureResult::new(async move {
  191. let client = try_get_client?;
  192. let params = InsertCollabParams::new(
  193. collab_object.object_id.clone(),
  194. collab_object.collab_type.clone(),
  195. data,
  196. collab_object.workspace_id.clone(),
  197. );
  198. client.create_collab(params).await?;
  199. Ok(())
  200. })
  201. }
  202. }
  203. pub async fn user_sign_up_request(
  204. client: Arc<AFCloudClient>,
  205. params: AFCloudOAuthParams,
  206. ) -> Result<AuthResponse, FlowyError> {
  207. user_sign_in_with_url(client, params).await
  208. }
  209. pub async fn user_sign_in_with_url(
  210. client: Arc<AFCloudClient>,
  211. params: AFCloudOAuthParams,
  212. ) -> Result<AuthResponse, FlowyError> {
  213. let is_new_user = client.sign_in_with_url(&params.sign_in_url).await?;
  214. let (profile, af_workspaces) = tokio::try_join!(client.profile(), client.workspaces())?;
  215. let latest_workspace = to_user_workspace(
  216. af_workspaces
  217. .get_latest(&profile)
  218. .or(af_workspaces.first().cloned())
  219. .ok_or(anyhow!("no workspace found"))?,
  220. )?;
  221. let user_workspaces = to_user_workspaces(af_workspaces)?;
  222. let encryption_type = encryption_type_from_profile(&profile);
  223. Ok(AuthResponse {
  224. user_id: profile.uid.ok_or(anyhow!("no uid found"))?,
  225. name: profile.name.ok_or(anyhow!("no name found"))?,
  226. latest_workspace,
  227. user_workspaces,
  228. email: profile.email,
  229. token: token_from_client(client.clone()).await,
  230. device_id: params.device_id,
  231. encryption_type,
  232. is_new_user,
  233. })
  234. }
  235. async fn token_from_client(client: Arc<AFCloudClient>) -> Option<String> {
  236. client.access_token().ok()
  237. }
  238. fn encryption_type_from_profile(profile: &AFUserProfileView) -> EncryptionType {
  239. match &profile.encryption_sign {
  240. Some(e) => EncryptionType::SelfEncryption(e.to_string()),
  241. None => EncryptionType::NoEncryption,
  242. }
  243. }
  244. fn to_user_workspace(af_workspace: AFWorkspace) -> Result<UserWorkspace, FlowyError> {
  245. Ok(UserWorkspace {
  246. id: af_workspace.workspace_id.to_string(),
  247. name: af_workspace
  248. .workspace_name
  249. .ok_or(anyhow!("no workspace_name found"))?,
  250. created_at: af_workspace
  251. .created_at
  252. .ok_or(anyhow!("no created_at found"))?,
  253. database_views_aggregate_id: af_workspace
  254. .database_storage_id
  255. .ok_or(anyhow!("no database_views_aggregate_id found"))?
  256. .to_string(),
  257. })
  258. }
  259. fn to_user_workspaces(af_workspaces: AFWorkspaces) -> Result<Vec<UserWorkspace>, FlowyError> {
  260. let mut result = Vec::with_capacity(af_workspaces.len());
  261. for item in af_workspaces.0.into_iter() {
  262. let user_workspace = to_user_workspace(item)?;
  263. result.push(user_workspace);
  264. }
  265. Ok(result)
  266. }
  267. fn oauth_params_from_box_any(any: BoxAny) -> Result<AFCloudOAuthParams, Error> {
  268. let map: HashMap<String, String> = any.unbox_or_error()?;
  269. let sign_in_url = map
  270. .get(USER_SIGN_IN_URL)
  271. .ok_or_else(|| FlowyError::new(ErrorCode::MissingAuthField, "Missing token field"))?
  272. .as_str();
  273. let device_id = map.get(USER_DEVICE_ID).cloned().unwrap_or_default();
  274. Ok(AFCloudOAuthParams {
  275. sign_in_url: sign_in_url.to_string(),
  276. device_id,
  277. })
  278. }