UserAccountManagerImpl.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /*
  2. * Nextcloud - Android Client
  3. *
  4. * SPDX-FileCopyrightText: 2023 TSI-mc
  5. * SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
  6. * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
  7. */
  8. package com.nextcloud.client.account;
  9. import android.accounts.Account;
  10. import android.accounts.AccountManager;
  11. import android.accounts.AccountManagerFuture;
  12. import android.accounts.AuthenticatorException;
  13. import android.accounts.OperationCanceledException;
  14. import android.app.Activity;
  15. import android.content.Context;
  16. import android.content.Intent;
  17. import android.content.SharedPreferences;
  18. import android.preference.PreferenceManager;
  19. import android.text.TextUtils;
  20. import com.nextcloud.common.NextcloudClient;
  21. import com.owncloud.android.MainApp;
  22. import com.owncloud.android.R;
  23. import com.owncloud.android.authentication.AuthenticatorActivity;
  24. import com.owncloud.android.datamodel.ArbitraryDataProvider;
  25. import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
  26. import com.owncloud.android.datamodel.OCFile;
  27. import com.owncloud.android.lib.common.OwnCloudAccount;
  28. import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
  29. import com.owncloud.android.lib.common.UserInfo;
  30. import com.owncloud.android.lib.common.accounts.AccountUtils;
  31. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  32. import com.owncloud.android.lib.common.utils.Log_OC;
  33. import com.owncloud.android.lib.resources.status.OwnCloudVersion;
  34. import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
  35. import java.io.IOException;
  36. import java.net.URI;
  37. import java.util.ArrayList;
  38. import java.util.Arrays;
  39. import java.util.List;
  40. import java.util.Optional;
  41. import javax.inject.Inject;
  42. import androidx.annotation.NonNull;
  43. import androidx.annotation.Nullable;
  44. public class UserAccountManagerImpl implements UserAccountManager {
  45. private static final String TAG = UserAccountManagerImpl.class.getSimpleName();
  46. private static final String PREF_SELECT_OC_ACCOUNT = "select_oc_account";
  47. private Context context;
  48. private final AccountManager accountManager;
  49. public static UserAccountManagerImpl fromContext(Context context) {
  50. AccountManager am = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
  51. return new UserAccountManagerImpl(context, am);
  52. }
  53. @Inject
  54. public UserAccountManagerImpl(
  55. Context context,
  56. AccountManager accountManager
  57. ) {
  58. this.context = context;
  59. this.accountManager = accountManager;
  60. }
  61. @Override
  62. public void removeAllAccounts() {
  63. for (Account account : getAccounts()) {
  64. accountManager.removeAccount(account, null, null);
  65. }
  66. }
  67. @Override
  68. public boolean removeUser(User user) {
  69. try {
  70. AccountManagerFuture<Boolean> result = accountManager.removeAccount(user.toPlatformAccount(),
  71. null,
  72. null);
  73. return result.getResult();
  74. } catch (OperationCanceledException| AuthenticatorException| IOException ex) {
  75. return false;
  76. }
  77. }
  78. @Override
  79. @NonNull
  80. public Account[] getAccounts() {
  81. return accountManager.getAccountsByType(getAccountType());
  82. }
  83. @Override
  84. @NonNull
  85. public List<User> getAllUsers() {
  86. Account[] accounts = getAccounts();
  87. List<User> users = new ArrayList<>(accounts.length);
  88. for (Account account : accounts) {
  89. User user = createUserFromAccount(account);
  90. if (user != null) {
  91. users.add(user);
  92. }
  93. }
  94. return users;
  95. }
  96. @Override
  97. public boolean exists(Account account) {
  98. Account[] nextcloudAccounts = getAccounts();
  99. if (account != null && account.name != null) {
  100. int lastAtPos = account.name.lastIndexOf('@');
  101. String hostAndPort = account.name.substring(lastAtPos + 1);
  102. String username = account.name.substring(0, lastAtPos);
  103. String otherHostAndPort;
  104. String otherUsername;
  105. for (Account otherAccount : nextcloudAccounts) {
  106. lastAtPos = otherAccount.name.lastIndexOf('@');
  107. otherHostAndPort = otherAccount.name.substring(lastAtPos + 1);
  108. otherUsername = otherAccount.name.substring(0, lastAtPos);
  109. if (otherHostAndPort.equals(hostAndPort) &&
  110. otherUsername.equalsIgnoreCase(username)) {
  111. return true;
  112. }
  113. }
  114. }
  115. return false;
  116. }
  117. @Override
  118. @NonNull
  119. public Account getCurrentAccount() {
  120. Account[] ocAccounts = getAccounts();
  121. ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
  122. SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
  123. String accountName = appPreferences.getString(PREF_SELECT_OC_ACCOUNT, null);
  124. Account defaultAccount = Arrays.stream(ocAccounts)
  125. .filter(account -> account.name.equals(accountName))
  126. .findFirst()
  127. .orElse(null);
  128. // take first which is not pending for removal account as fallback
  129. if (defaultAccount == null) {
  130. defaultAccount = Arrays.stream(ocAccounts)
  131. .filter(account -> !arbitraryDataProvider.getBooleanValue(account.name, PENDING_FOR_REMOVAL))
  132. .findFirst()
  133. .orElse(null);
  134. }
  135. if (defaultAccount == null) {
  136. defaultAccount = ocAccounts[0];
  137. }
  138. return defaultAccount;
  139. }
  140. /**
  141. * Temporary solution to convert platform account to user instance.
  142. * It takes null and returns null on error to ease error handling
  143. * in legacy code.
  144. *
  145. * @param account Account instance
  146. * @return User instance or null, if conversion failed
  147. */
  148. @Nullable
  149. private User createUserFromAccount(@Nullable Account account) {
  150. if (account == null) {
  151. return null;
  152. }
  153. if (context == null) {
  154. Log_OC.d(TAG, "Context is null MainApp.getAppContext() used");
  155. context = MainApp.getAppContext();
  156. }
  157. OwnCloudAccount ownCloudAccount;
  158. try {
  159. ownCloudAccount = new OwnCloudAccount(account, context);
  160. } catch (AccountUtils.AccountNotFoundException ex) {
  161. return null;
  162. }
  163. /*
  164. * Server version
  165. */
  166. String serverVersionStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_VERSION);
  167. OwnCloudVersion serverVersion;
  168. if (serverVersionStr != null) {
  169. serverVersion = new OwnCloudVersion(serverVersionStr);
  170. } else {
  171. serverVersion = MainApp.MINIMUM_SUPPORTED_SERVER_VERSION;
  172. }
  173. /*
  174. * Server address
  175. */
  176. String serverAddressStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
  177. if (serverAddressStr == null || serverAddressStr.isEmpty()) {
  178. return AnonymousUser.fromContext(context);
  179. }
  180. URI serverUri = URI.create(serverAddressStr); // TODO: validate
  181. return new RegisteredUser(
  182. account,
  183. ownCloudAccount,
  184. new Server(serverUri, serverVersion)
  185. );
  186. }
  187. /**
  188. * Get user. If user cannot be retrieved due to data error, anonymous user is returned instead.
  189. *
  190. *
  191. * @return User instance
  192. */
  193. @NonNull
  194. @Override
  195. public User getUser() {
  196. Account account = getCurrentAccount();
  197. User user = createUserFromAccount(account);
  198. return user != null ? user : AnonymousUser.fromContext(context);
  199. }
  200. @Override
  201. @NonNull
  202. public Optional<User> getUser(CharSequence accountName) {
  203. Account account = getAccountByName(accountName.toString());
  204. User user = createUserFromAccount(account);
  205. return Optional.ofNullable(user);
  206. }
  207. @Override
  208. public User getAnonymousUser() {
  209. return AnonymousUser.fromContext(context);
  210. }
  211. @Override
  212. @Nullable
  213. public OwnCloudAccount getCurrentOwnCloudAccount() {
  214. try {
  215. Account currentPlatformAccount = getCurrentAccount();
  216. return new OwnCloudAccount(currentPlatformAccount, context);
  217. } catch (AccountUtils.AccountNotFoundException | IllegalArgumentException ex) {
  218. return null;
  219. }
  220. }
  221. @Override
  222. @Nullable
  223. public Account getAccountByName(String name) {
  224. for (Account account : getAccounts()) {
  225. if (account.name.equals(name)) {
  226. return account;
  227. }
  228. }
  229. return null;
  230. }
  231. @Override
  232. public boolean setCurrentOwnCloudAccount(String accountName) {
  233. boolean result = false;
  234. if (accountName != null) {
  235. for (final Account account : getAccounts()) {
  236. if (accountName.equals(account.name)) {
  237. SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
  238. appPrefs.putString(PREF_SELECT_OC_ACCOUNT, accountName);
  239. appPrefs.apply();
  240. result = true;
  241. break;
  242. }
  243. }
  244. }
  245. return result;
  246. }
  247. @Override
  248. public boolean setCurrentOwnCloudAccount(int hashCode) {
  249. boolean result = false;
  250. if (hashCode != 0) {
  251. for (final User user : getAllUsers()) {
  252. if (hashCode == user.hashCode()) {
  253. SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
  254. appPrefs.putString(PREF_SELECT_OC_ACCOUNT, user.getAccountName());
  255. appPrefs.apply();
  256. result = true;
  257. break;
  258. }
  259. }
  260. }
  261. return result;
  262. }
  263. @Deprecated
  264. @Override
  265. @NonNull
  266. public OwnCloudVersion getServerVersion(Account account) {
  267. OwnCloudVersion serverVersion = MainApp.MINIMUM_SUPPORTED_SERVER_VERSION;
  268. if (account != null) {
  269. AccountManager accountMgr = AccountManager.get(MainApp.getAppContext());
  270. String serverVersionStr = accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_OC_VERSION);
  271. if (serverVersionStr != null) {
  272. serverVersion = new OwnCloudVersion(serverVersionStr);
  273. }
  274. }
  275. return serverVersion;
  276. }
  277. @Override
  278. public void resetOwnCloudAccount() {
  279. SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
  280. appPrefs.putString(PREF_SELECT_OC_ACCOUNT, null);
  281. appPrefs.apply();
  282. }
  283. @Override
  284. public boolean accountOwnsFile(OCFile file, Account account) {
  285. final String ownerId = file.getOwnerId();
  286. return TextUtils.isEmpty(ownerId) || account.name.split("@")[0].equalsIgnoreCase(ownerId);
  287. }
  288. @Override
  289. public boolean userOwnsFile(OCFile file, User user) {
  290. return accountOwnsFile(file, user.toPlatformAccount());
  291. }
  292. public boolean migrateUserId() {
  293. Account[] ocAccounts = accountManager.getAccountsByType(MainApp.getAccountType(context));
  294. String userId;
  295. String displayName;
  296. GetUserInfoRemoteOperation remoteUserNameOperation = new GetUserInfoRemoteOperation();
  297. int failed = 0;
  298. for (Account account : ocAccounts) {
  299. String storedUserId = accountManager.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
  300. if (!TextUtils.isEmpty(storedUserId)) {
  301. continue;
  302. }
  303. // add userId
  304. try {
  305. OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
  306. NextcloudClient nextcloudClient = OwnCloudClientManagerFactory
  307. .getDefaultSingleton()
  308. .getNextcloudClientFor(ocAccount, context);
  309. RemoteOperationResult<UserInfo> result = remoteUserNameOperation.execute(nextcloudClient);
  310. if (result.isSuccess()) {
  311. UserInfo userInfo = result.getResultData();
  312. userId = userInfo.getId();
  313. displayName = userInfo.getDisplayName();
  314. } else {
  315. // skip account, try it next time
  316. Log_OC.e(TAG, "Error while getting username for account: " + account.name);
  317. failed++;
  318. continue;
  319. }
  320. } catch (Exception e) {
  321. Log_OC.e(TAG, "Error while getting username: " + e.getMessage());
  322. failed++;
  323. continue;
  324. }
  325. accountManager.setUserData(account,
  326. com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_DISPLAY_NAME,
  327. displayName);
  328. accountManager.setUserData(account,
  329. com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID,
  330. userId);
  331. }
  332. return failed == 0;
  333. }
  334. private String getAccountType() {
  335. return context.getString(R.string.account_type);
  336. }
  337. @Override
  338. public void startAccountCreation(final Activity activity) {
  339. Intent intent = new Intent(context, AuthenticatorActivity.class);
  340. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  341. context.startActivity(intent);
  342. }
  343. }