465 lines
16 KiB
Java
465 lines
16 KiB
Java
/*
|
|
* Nextcloud - Android Client
|
|
*
|
|
* SPDX-FileCopyrightText: 2023-2024 TSI-mc <surinder.kumar@t-systems.com>
|
|
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
|
|
*/
|
|
package com.nextcloud.client.account;
|
|
|
|
import android.accounts.Account;
|
|
import android.accounts.AccountManager;
|
|
import android.accounts.AccountManagerFuture;
|
|
import android.accounts.AuthenticatorException;
|
|
import android.accounts.OperationCanceledException;
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.preference.PreferenceManager;
|
|
import android.text.TextUtils;
|
|
|
|
import com.nextcloud.client.onboarding.FirstRunActivity;
|
|
import com.nextcloud.common.NextcloudClient;
|
|
import com.nextcloud.utils.extensions.AccountExtensionsKt;
|
|
import com.nmc.android.ui.LauncherActivity;
|
|
import com.owncloud.android.MainApp;
|
|
import com.owncloud.android.R;
|
|
import com.owncloud.android.authentication.AuthenticatorActivity;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
|
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
|
import com.owncloud.android.datamodel.OCFile;
|
|
import com.owncloud.android.lib.common.OwnCloudAccount;
|
|
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
|
import com.owncloud.android.lib.common.UserInfo;
|
|
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
|
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
|
import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
|
|
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
public class UserAccountManagerImpl implements UserAccountManager {
|
|
|
|
private static final String TAG = UserAccountManagerImpl.class.getSimpleName();
|
|
private static final String PREF_SELECT_OC_ACCOUNT = "select_oc_account";
|
|
|
|
private Context context;
|
|
private final AccountManager accountManager;
|
|
|
|
public static UserAccountManagerImpl fromContext(Context context) {
|
|
AccountManager am = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
|
|
return new UserAccountManagerImpl(context, am);
|
|
}
|
|
|
|
@Inject
|
|
public UserAccountManagerImpl(
|
|
Context context,
|
|
AccountManager accountManager
|
|
) {
|
|
this.context = context;
|
|
this.accountManager = accountManager;
|
|
}
|
|
|
|
@Override
|
|
public void removeAllAccounts() {
|
|
for (Account account : getAccounts()) {
|
|
accountManager.removeAccount(account, null, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean removeUser(User user) {
|
|
try {
|
|
AccountManagerFuture<Boolean> result = accountManager.removeAccount(user.toPlatformAccount(),
|
|
null,
|
|
null);
|
|
return result.getResult();
|
|
} catch (OperationCanceledException| AuthenticatorException| IOException ex) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public Account[] getAccounts() {
|
|
return accountManager.getAccountsByType(getAccountType());
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public List<User> getAllUsers() {
|
|
Account[] accounts = getAccounts();
|
|
List<User> users = new ArrayList<>(accounts.length);
|
|
for (Account account : accounts) {
|
|
User user = createUserFromAccount(account);
|
|
if (user != null) {
|
|
users.add(user);
|
|
}
|
|
}
|
|
return users;
|
|
}
|
|
|
|
@Override
|
|
public boolean exists(Account account) {
|
|
try {
|
|
if (account == null) {
|
|
Log_OC.d(TAG, "account is null");
|
|
return false;
|
|
}
|
|
|
|
Account[] nextcloudAccounts = getAccounts();
|
|
if (nextcloudAccounts.length == 0) {
|
|
Log_OC.d(TAG, "nextcloudAccounts are empty");
|
|
return false;
|
|
}
|
|
|
|
if (account.name.isEmpty()) {
|
|
Log_OC.d(TAG, "account name is empty");
|
|
return false;
|
|
}
|
|
|
|
int lastAtPos = account.name.lastIndexOf('@');
|
|
if (lastAtPos == -1) {
|
|
Log_OC.d(TAG, "lastAtPos cannot be found");
|
|
return false;
|
|
}
|
|
|
|
boolean isLastAtPosInBoundsForHostAndPort = lastAtPos + 1 < account.name.length();
|
|
if (!isLastAtPosInBoundsForHostAndPort) {
|
|
Log_OC.d(TAG, "lastAtPos not in bounds");
|
|
return false;
|
|
}
|
|
|
|
String hostAndPort = account.name.substring(lastAtPos + 1);
|
|
|
|
String username = account.name.substring(0, lastAtPos);
|
|
if (hostAndPort.isEmpty() || username.isEmpty()) {
|
|
Log_OC.d(TAG, "hostAndPort or username is empty");
|
|
return false;
|
|
}
|
|
|
|
String otherHostAndPort;
|
|
String otherUsername;
|
|
|
|
for (Account otherAccount : nextcloudAccounts) {
|
|
// Skip null accounts or accounts with null names
|
|
if (otherAccount == null || otherAccount.name.isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
lastAtPos = otherAccount.name.lastIndexOf('@');
|
|
|
|
// Skip invalid account names
|
|
if (lastAtPos == -1) {
|
|
continue;
|
|
}
|
|
|
|
boolean isLastAtPosInBoundsForOtherHostAndPort = lastAtPos + 1 < otherAccount.name.length();
|
|
if (!isLastAtPosInBoundsForOtherHostAndPort) {
|
|
continue;
|
|
}
|
|
otherHostAndPort = otherAccount.name.substring(lastAtPos + 1);
|
|
|
|
otherUsername = otherAccount.name.substring(0, lastAtPos);
|
|
|
|
if (otherHostAndPort.equals(hostAndPort) &&
|
|
otherUsername.equalsIgnoreCase(username)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} catch (Exception e) {
|
|
Log_OC.d(TAG, "Exception caught at UserAccountManagerImpl.exists(): " + e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public Account getCurrentAccount() {
|
|
Account[] ocAccounts = getAccounts();
|
|
|
|
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
|
|
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
String accountName = appPreferences.getString(PREF_SELECT_OC_ACCOUNT, null);
|
|
|
|
Account defaultAccount = Arrays.stream(ocAccounts)
|
|
.filter(account -> account.name.equals(accountName))
|
|
.findFirst()
|
|
.orElse(null);
|
|
|
|
// take first which is not pending for removal account as fallback
|
|
if (defaultAccount == null) {
|
|
defaultAccount = Arrays.stream(ocAccounts)
|
|
.filter(account -> !arbitraryDataProvider.getBooleanValue(account.name, PENDING_FOR_REMOVAL))
|
|
.findFirst()
|
|
.orElse(null);
|
|
}
|
|
|
|
if (defaultAccount == null) {
|
|
if (ocAccounts.length > 0) {
|
|
defaultAccount = ocAccounts[0];
|
|
} else {
|
|
defaultAccount = getAnonymousAccount();
|
|
}
|
|
}
|
|
|
|
return defaultAccount;
|
|
}
|
|
|
|
private Account getAnonymousAccount() {
|
|
return new Account("Anonymous", context.getString(R.string.anonymous_account_type));
|
|
}
|
|
|
|
/**
|
|
* Temporary solution to convert platform account to user instance.
|
|
* It takes null and returns null on error to ease error handling
|
|
* in legacy code.
|
|
*
|
|
* @param account Account instance
|
|
* @return User instance or null, if conversion failed
|
|
*/
|
|
@Nullable
|
|
private User createUserFromAccount(@NonNull Account account) {
|
|
Context safeContext = context != null ? context : MainApp.getAppContext();
|
|
if (safeContext == null) {
|
|
Log_OC.e(TAG, "Unable to obtain a valid context");
|
|
return null;
|
|
}
|
|
|
|
if (AccountExtensionsKt.isAnonymous(account, safeContext)) {
|
|
return null;
|
|
}
|
|
|
|
OwnCloudAccount ownCloudAccount;
|
|
try {
|
|
ownCloudAccount = new OwnCloudAccount(account, safeContext);
|
|
} catch (Exception ex) {
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* Server version
|
|
*/
|
|
String serverVersionStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_VERSION);
|
|
OwnCloudVersion serverVersion;
|
|
if (serverVersionStr != null) {
|
|
serverVersion = new OwnCloudVersion(serverVersionStr);
|
|
} else {
|
|
serverVersion = MainApp.MINIMUM_SUPPORTED_SERVER_VERSION;
|
|
}
|
|
|
|
/*
|
|
* Server address
|
|
*/
|
|
String serverAddressStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
|
|
if (serverAddressStr == null || serverAddressStr.isEmpty()) {
|
|
return AnonymousUser.fromContext(safeContext);
|
|
}
|
|
URI serverUri = URI.create(serverAddressStr); // TODO: validate
|
|
|
|
return new RegisteredUser(
|
|
account,
|
|
ownCloudAccount,
|
|
new Server(serverUri, serverVersion)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get user. If user cannot be retrieved due to data error, anonymous user is returned instead.
|
|
*
|
|
*
|
|
* @return User instance
|
|
*/
|
|
@NonNull
|
|
@Override
|
|
public User getUser() {
|
|
Account account = getCurrentAccount();
|
|
User user = createUserFromAccount(account);
|
|
return user != null ? user : AnonymousUser.fromContext(context);
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public Optional<User> getUser(CharSequence accountName) {
|
|
Account account = getAccountByName(accountName.toString());
|
|
User user = createUserFromAccount(account);
|
|
return Optional.ofNullable(user);
|
|
}
|
|
|
|
@Override
|
|
public User getAnonymousUser() {
|
|
return AnonymousUser.fromContext(context);
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public OwnCloudAccount getCurrentOwnCloudAccount() {
|
|
try {
|
|
Account currentPlatformAccount = getCurrentAccount();
|
|
return new OwnCloudAccount(currentPlatformAccount, context);
|
|
} catch (AccountUtils.AccountNotFoundException | IllegalArgumentException ex) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@NonNull
|
|
public Account getAccountByName(String name) {
|
|
for (Account account : getAccounts()) {
|
|
if (account.name.equals(name)) {
|
|
return account;
|
|
}
|
|
}
|
|
|
|
return getAnonymousAccount();
|
|
}
|
|
|
|
@Override
|
|
public boolean setCurrentOwnCloudAccount(String accountName) {
|
|
boolean result = false;
|
|
if (accountName != null) {
|
|
for (final Account account : getAccounts()) {
|
|
if (accountName.equals(account.name)) {
|
|
SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
|
appPrefs.putString(PREF_SELECT_OC_ACCOUNT, accountName);
|
|
appPrefs.apply();
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean setCurrentOwnCloudAccount(int hashCode) {
|
|
boolean result = false;
|
|
if (hashCode != 0) {
|
|
for (final User user : getAllUsers()) {
|
|
if (hashCode == user.hashCode()) {
|
|
SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
|
appPrefs.putString(PREF_SELECT_OC_ACCOUNT, user.getAccountName());
|
|
appPrefs.apply();
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Deprecated
|
|
@Override
|
|
@NonNull
|
|
public OwnCloudVersion getServerVersion(Account account) {
|
|
OwnCloudVersion serverVersion = MainApp.MINIMUM_SUPPORTED_SERVER_VERSION;
|
|
|
|
if (account != null) {
|
|
AccountManager accountMgr = AccountManager.get(MainApp.getAppContext());
|
|
String serverVersionStr = accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_OC_VERSION);
|
|
|
|
if (serverVersionStr != null) {
|
|
serverVersion = new OwnCloudVersion(serverVersionStr);
|
|
}
|
|
}
|
|
|
|
return serverVersion;
|
|
}
|
|
|
|
@Override
|
|
public void resetOwnCloudAccount() {
|
|
SharedPreferences.Editor appPrefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
|
appPrefs.putString(PREF_SELECT_OC_ACCOUNT, null);
|
|
appPrefs.apply();
|
|
}
|
|
|
|
@Override
|
|
public boolean accountOwnsFile(OCFile file, Account account) {
|
|
final String ownerId = file.getOwnerId();
|
|
return TextUtils.isEmpty(ownerId) || account.name.split("@")[0].equalsIgnoreCase(ownerId);
|
|
}
|
|
|
|
@Override
|
|
public boolean userOwnsFile(OCFile file, User user) {
|
|
return accountOwnsFile(file, user.toPlatformAccount());
|
|
}
|
|
|
|
public boolean migrateUserId() {
|
|
Account[] ocAccounts = accountManager.getAccountsByType(MainApp.getAccountType(context));
|
|
String userId;
|
|
String displayName;
|
|
GetUserInfoRemoteOperation remoteUserNameOperation = new GetUserInfoRemoteOperation();
|
|
int failed = 0;
|
|
for (Account account : ocAccounts) {
|
|
String storedUserId = accountManager.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
|
|
|
|
if (!TextUtils.isEmpty(storedUserId)) {
|
|
continue;
|
|
}
|
|
|
|
// add userId
|
|
try {
|
|
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
|
|
NextcloudClient nextcloudClient = OwnCloudClientManagerFactory
|
|
.getDefaultSingleton()
|
|
.getNextcloudClientFor(ocAccount, context);
|
|
|
|
RemoteOperationResult<UserInfo> result = remoteUserNameOperation.execute(nextcloudClient);
|
|
|
|
if (result.isSuccess()) {
|
|
UserInfo userInfo = result.getResultData();
|
|
userId = userInfo.getId();
|
|
displayName = userInfo.getDisplayName();
|
|
} else {
|
|
// skip account, try it next time
|
|
Log_OC.e(TAG, "Error while getting username for account: " + account.name);
|
|
failed++;
|
|
continue;
|
|
}
|
|
} catch (Exception e) {
|
|
Log_OC.e(TAG, "Error while getting username: " + e.getMessage());
|
|
failed++;
|
|
continue;
|
|
}
|
|
|
|
accountManager.setUserData(account,
|
|
com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_DISPLAY_NAME,
|
|
displayName);
|
|
accountManager.setUserData(account,
|
|
com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID,
|
|
userId);
|
|
}
|
|
|
|
return failed == 0;
|
|
}
|
|
|
|
private String getAccountType() {
|
|
return context.getString(R.string.account_type);
|
|
}
|
|
|
|
@Override
|
|
public void startAccountCreation(final Activity activity) {
|
|
|
|
// skipping AuthenticatorActivity redirection when user is on Launcher or FirstRun Activity
|
|
if (activity instanceof LauncherActivity || activity instanceof FirstRunActivity) return;
|
|
|
|
Intent intent = new Intent(context, AuthenticatorActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
context.startActivity(intent);
|
|
}
|
|
}
|