UserAccountManagerImpl.java 14 KB

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