Merge pull request #3889 from nextcloud/checkUserId

Cleanup authentication
This commit is contained in:
Andy Scherzinger 2019-04-15 22:51:08 +02:00 committed by GitHub
commit 03e04e516e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 114 additions and 1445 deletions

View file

@ -59,6 +59,9 @@
</value> </value>
</option> </option>
</JavaCodeStyleSettings> </JavaCodeStyleSettings>
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="120" />
</MarkdownNavigatorCodeStyleSettings>
<Objective-C-extensions> <Objective-C-extensions>
<file> <file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" /> <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
@ -204,4 +207,4 @@
</arrangement> </arrangement>
</codeStyleSettings> </codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

View file

@ -1 +1 @@
439 436

View file

@ -40,14 +40,6 @@
android:exported="true" android:exported="true"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Theme.ownCloud.noActionBar.Login"> android:theme="@style/Theme.ownCloud.noActionBar.Login">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/oauth2_redirect_scheme" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="com.owncloud.android.workaround.accounts.CREATE" /> <action android:name="com.owncloud.android.workaround.accounts.CREATE" />

View file

@ -265,14 +265,6 @@
android:launchMode="singleTask" android:launchMode="singleTask"
android:configChanges="orientation|screenSize|keyboardHidden" android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/Theme.ownCloud.noActionBar.Login"> android:theme="@style/Theme.ownCloud.noActionBar.Login">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/oauth2_redirect_scheme" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="com.owncloud.android.workaround.accounts.CREATE" /> <action android:name="com.owncloud.android.workaround.accounts.CREATE" />

View file

@ -170,23 +170,6 @@ public class UserAccountManagerImpl implements UserAccountManager {
accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_COOKIES) accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_COOKIES)
); );
// copy type of authentication
final String isSamlStr = accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO);
if (Boolean.parseBoolean(isSamlStr)) {
accountMgr.setUserData(newAccount, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE");
}
final String isOauthStr = accountMgr.getUserData(account, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
if (Boolean.parseBoolean(isOauthStr)) {
accountMgr.setUserData(newAccount, com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_SUPPORTS_OAUTH2, "TRUE");
}
/* TODO - study if it's possible to run this method in a background thread to copy the authToken
if (isOAuth || isSaml) {
accountMgr.setAuthToken(newAccount, mAuthTokenType, mAuthToken);
}
*/
// don't forget the account saved in preferences as the current one // don't forget the account saved in preferences as the current one
if (currentAccount.name.equals(account.name)) { if (currentAccount.name.equals(account.name)) {
AccountUtils.setCurrentOwnCloudAccount(context, newAccountName); AccountUtils.setCurrentOwnCloudAccount(context, newAccountName);

View file

@ -64,7 +64,6 @@ import com.owncloud.android.datastorage.StoragePoint;
import com.owncloud.android.jobs.MediaFoldersDetectionJob; import com.owncloud.android.jobs.MediaFoldersDetectionJob;
import com.owncloud.android.jobs.NCJobCreator; import com.owncloud.android.jobs.NCJobCreator;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.ui.activity.ContactsPreferenceActivity; import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
@ -198,16 +197,8 @@ public class MainApp extends MultiDexApplication implements
MainApp.storagePath = preferences.getStoragePath(getApplicationContext().getFilesDir().getAbsolutePath()); MainApp.storagePath = preferences.getStoragePath(getApplicationContext().getFilesDir().getAbsolutePath());
boolean isSamlAuth = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
OwnCloudClientManagerFactory.setUserAgent(getUserAgent()); OwnCloudClientManagerFactory.setUserAgent(getUserAgent());
OwnCloudClientManagerFactory.setNextcloudUserAgent(getNextcloudUserAgent()); OwnCloudClientManagerFactory.setNextcloudUserAgent(getNextcloudUserAgent());
if (isSamlAuth) {
OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT);
} else {
OwnCloudClientManagerFactory
.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT_IF_SERVER_SUPPORTS_SERVER_MONITORING);
}
// initialise thumbnails cache on background thread // initialise thumbnails cache on background thread
new ThumbnailsCacheManager.InitDiskCacheTask().execute(); new ThumbnailsCacheManager.InitDiskCacheTask().execute();

View file

@ -40,15 +40,15 @@ import com.owncloud.android.lib.common.utils.Log_OC;
/** /**
* Authenticator for ownCloud accounts. * Authenticator for ownCloud accounts.
* *
* Controller class accessed from the system AccountManager, * Controller class accessed from the system AccountManager,
* providing integration of ownCloud accounts with the Android system. * providing integration of ownCloud accounts with the Android system.
* *
* TODO - better separation in operations for OAuth-capable and regular ownCloud accounts. * TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
* TODO - review completeness * TODO - review completeness
*/ */
public class AccountAuthenticator extends AbstractAccountAuthenticator { public class AccountAuthenticator extends AbstractAccountAuthenticator {
/** /**
* Is used by android system to assign accounts to authenticators. * Is used by android system to assign accounts to authenticators.
* Should be used by application and all extensions. * Should be used by application and all extensions.
@ -59,9 +59,9 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
public static final String KEY_ACCOUNT = "account"; public static final String KEY_ACCOUNT = "account";
private static final String TAG = AccountAuthenticator.class.getSimpleName(); private static final String TAG = AccountAuthenticator.class.getSimpleName();
private Context mContext; private Context mContext;
private Handler mHandler; private Handler mHandler;
public AccountAuthenticator(Context context) { public AccountAuthenticator(Context context) {
@ -101,17 +101,17 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE); intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
setIntentFlags(intent); setIntentFlags(intent);
bundle.putParcelable(AccountManager.KEY_INTENT, intent); bundle.putParcelable(AccountManager.KEY_INTENT, intent);
} else { } else {
// Return an error // Return an error
bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION); bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name)); final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name));
bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message); bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
mHandler.post(() -> Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show()); mHandler.post(() -> Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show());
} }
return bundle; return bundle;
} }
@ -160,7 +160,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
Log_OC.e(TAG, "Failed to validate account type " + account.type + ": " + e.getMessage(), e); Log_OC.e(TAG, "Failed to validate account type " + account.type + ": " + e.getMessage(), e);
return e.getFailureBundle(); return e.getFailureBundle();
} }
/// check if required token is stored /// check if required token is stored
final AccountManager am = AccountManager.get(mContext); final AccountManager am = AccountManager.get(mContext);
String accessToken; String accessToken;
@ -176,7 +176,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
result.putString(AccountManager.KEY_AUTHTOKEN, accessToken); result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
return result; return result;
} }
/// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
Intent intent = new Intent(mContext, AuthenticatorActivity.class); Intent intent = new Intent(mContext, AuthenticatorActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
@ -184,7 +184,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
intent.putExtra(KEY_LOGIN_OPTIONS, options); intent.putExtra(KEY_LOGIN_OPTIONS, options);
intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account); intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN); intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent); bundle.putParcelable(AccountManager.KEY_INTENT, intent);
@ -242,10 +242,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
String accountType = MainApp.getAccountType(mContext); String accountType = MainApp.getAccountType(mContext);
if (!authTokenType.equals(accountType) && if (!authTokenType.equals(accountType) &&
!authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(accountType)) && !authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(accountType))) {
!authTokenType.equals(AccountTypeUtils.getAuthTokenTypeAccessToken(accountType)) &&
!authTokenType.equals(AccountTypeUtils.getAuthTokenTypeRefreshToken(accountType)) &&
!authTokenType.equals(AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(accountType))) {
throw new UnsupportedAuthTokenTypeException(); throw new UnsupportedAuthTokenTypeException();
} }
} }

View file

@ -44,7 +44,6 @@ import android.Manifest;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -55,6 +54,7 @@ import android.content.pm.PackageManager;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.net.http.SslCertificate;
import android.net.http.SslError; import android.net.http.SslError;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -78,7 +78,6 @@ import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler; import android.webkit.SslErrorHandler;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.CheckBox;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@ -91,13 +90,11 @@ import com.google.android.material.textfield.TextInputLayout;
import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.account.UserAccountManager;
import com.owncloud.android.MainApp; import com.owncloud.android.MainApp;
import com.owncloud.android.R; import com.owncloud.android.R;
import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.OwnCloudCredentials;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.UserInfo;
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
import com.owncloud.android.lib.common.network.CertificateCombinedException; import com.owncloud.android.lib.common.network.CertificateCombinedException;
@ -111,7 +108,6 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation;
import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod; import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod;
import com.owncloud.android.operations.GetServerInfoOperation; import com.owncloud.android.operations.GetServerInfoOperation;
import com.owncloud.android.operations.OAuth2GetAccessToken;
import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.FileDisplayActivity;
@ -119,16 +115,18 @@ import com.owncloud.android.ui.activity.FirstRunActivity;
import com.owncloud.android.ui.components.CustomEditText; import com.owncloud.android.ui.components.CustomEditText;
import com.owncloud.android.ui.dialog.CredentialsDialogFragment; import com.owncloud.android.ui.dialog.CredentialsDialogFragment;
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog; import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
import com.owncloud.android.ui.dialog.SamlWebViewDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener; import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.PermissionUtil;
import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.security.SecureRandom; import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@ -148,8 +146,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
* This Activity is used to add an ownCloud account to the App * This Activity is used to add an ownCloud account to the App
*/ */
public class AuthenticatorActivity extends AccountAuthenticatorActivity public class AuthenticatorActivity extends AccountAuthenticatorActivity
implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener, implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener, OnSslUntrustedCertListener,
SsoWebViewClientListener, OnSslUntrustedCertListener,
AuthenticatorAsyncTask.OnAuthenticatorTaskListener { AuthenticatorAsyncTask.OnAuthenticatorTaskListener {
private static final String TAG = AuthenticatorActivity.class.getSimpleName(); private static final String TAG = AuthenticatorActivity.class.getSimpleName();
@ -158,8 +155,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
public static final String EXTRA_ACCOUNT = "ACCOUNT"; public static final String EXTRA_ACCOUNT = "ACCOUNT";
public static final String EXTRA_USE_PROVIDER_AS_WEBLOGIN = "USE_PROVIDER_AS_WEBLOGIN"; public static final String EXTRA_USE_PROVIDER_AS_WEBLOGIN = "USE_PROVIDER_AS_WEBLOGIN";
private static final String KEY_AUTH_TOKEN_TYPE = "AUTH_TOKEN_TYPE";
private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT"; private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";
private static final String KEY_OC_VERSION = "OC_VERSION"; private static final String KEY_OC_VERSION = "OC_VERSION";
private static final String KEY_SERVER_VALID = "SERVER_VALID"; private static final String KEY_SERVER_VALID = "SERVER_VALID";
@ -174,14 +169,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID"; private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
private static final String KEY_AUTH_TOKEN = "AUTH_TOKEN"; private static final String KEY_AUTH_TOKEN = "AUTH_TOKEN";
private static final String AUTH_ON = "on";
private static final String AUTH_OPTIONAL = "optional";
public static final byte ACTION_CREATE = 0; public static final byte ACTION_CREATE = 0;
public static final byte ACTION_UPDATE_EXPIRED_TOKEN = 2; // detected by the app public static final byte ACTION_UPDATE_EXPIRED_TOKEN = 2; // detected by the app
private static final String UNTRUSTED_CERT_DIALOG_TAG = "UNTRUSTED_CERT_DIALOG"; private static final String UNTRUSTED_CERT_DIALOG_TAG = "UNTRUSTED_CERT_DIALOG";
private static final String SAML_DIALOG_TAG = "SAML_DIALOG";
private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG"; private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
private static final String CREDENTIALS_DIALOG_TAG = "CREDENTIALS_DIALOG"; private static final String CREDENTIALS_DIALOG_TAG = "CREDENTIALS_DIALOG";
private static final String KEY_AUTH_IS_FIRST_ATTEMPT_TAG = "KEY_AUTH_IS_FIRST_ATTEMPT"; private static final String KEY_AUTH_IS_FIRST_ATTEMPT_TAG = "KEY_AUTH_IS_FIRST_ATTEMPT";
@ -208,14 +199,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
/// parameters from EXTRAs in starter Intent /// parameters from EXTRAs in starter Intent
private byte mAction; private byte mAction;
private Account mAccount; private Account mAccount;
private String mAuthTokenType;
/// activity-level references / state /// activity-level references / state
private final Handler mHandler = new Handler(); private final Handler mHandler = new Handler();
private ServiceConnection mOperationsServiceConnection; private ServiceConnection mOperationsServiceConnection;
private OperationsServiceBinder mOperationsServiceBinder; private OperationsServiceBinder mOperationsServiceBinder;
private AccountManager mAccountMgr; private AccountManager mAccountMgr;
private Uri mNewCapturedUriFromOAuth2Redirection;
/// Server PRE-Fragment elements /// Server PRE-Fragment elements
private CustomEditText mHostUrlInput; private CustomEditText mHostUrlInput;
@ -232,9 +221,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
private GetServerInfoOperation.ServerInfo mServerInfo = new GetServerInfoOperation.ServerInfo(); private GetServerInfoOperation.ServerInfo mServerInfo = new GetServerInfoOperation.ServerInfo();
/// Authentication PRE-Fragment elements /// Authentication PRE-Fragment elements
private CheckBox mOAuth2Check;
private TextView mOAuthAuthEndpointText;
private TextView mOAuthTokenEndpointText;
private EditText mUsernameInput; private EditText mUsernameInput;
private EditText mPasswordInput; private EditText mPasswordInput;
private View mOkButton; private View mOkButton;
@ -254,10 +240,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
/// Identifier of operation in progress which result shouldn't be lost /// Identifier of operation in progress which result shouldn't be lost
private long mWaitingForOpId = Long.MAX_VALUE; private long mWaitingForOpId = Long.MAX_VALUE;
private String basicTokenType;
private String oauthTokenType;
private String samlTokenType;
private boolean webViewLoginMethod; private boolean webViewLoginMethod;
private String webViewUser; private String webViewUser;
private String webViewPassword; private String webViewPassword;
@ -284,10 +266,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
FirstRunActivity.runIfNeeded(this); FirstRunActivity.runIfNeeded(this);
} }
basicTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType(this));
oauthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType(this));
samlTokenType = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType(this));
// delete cookies for webView // delete cookies for webView
deleteCookies(); deleteCookies();
@ -304,15 +282,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
/// init activity state /// init activity state
mAccountMgr = AccountManager.get(this); mAccountMgr = AccountManager.get(this);
mNewCapturedUriFromOAuth2Redirection = null;
/// get input values /// get input values
mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE);
mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT); mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
if (savedInstanceState == null) { if (savedInstanceState != null) {
initAuthTokenType();
} else {
mAuthTokenType = savedInstanceState.getString(KEY_AUTH_TOKEN_TYPE);
mWaitingForOpId = savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID); mWaitingForOpId = savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID);
mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG); mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG);
} }
@ -428,7 +402,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mPasswordInputLayout.setVisibility(View.VISIBLE); mPasswordInputLayout.setVisibility(View.VISIBLE);
mUsernameInputLayout.setVisibility(View.VISIBLE); mUsernameInputLayout.setVisibility(View.VISIBLE);
mUsernameInput.requestFocus(); mUsernameInput.requestFocus();
mOAuth2Check.setVisibility(View.INVISIBLE);
mAuthStatusView.setVisibility(View.INVISIBLE); mAuthStatusView.setVisibility(View.INVISIBLE);
mServerStatusView.setVisibility(View.INVISIBLE); mServerStatusView.setVisibility(View.INVISIBLE);
mTestServerButton.setVisibility(View.INVISIBLE); mTestServerButton.setVisibility(View.INVISIBLE);
@ -486,7 +459,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
@Override @Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
X509Certificate cert = SsoWebViewClient.getX509CertificateFromError(error); X509Certificate cert = getX509CertificateFromError(error);
try { try {
if (cert != null && NetworkUtils.isCertInKnownServersStore(cert, getApplicationContext())) { if (cert != null && NetworkUtils.isCertInKnownServersStore(cert, getApplicationContext())) {
@ -591,39 +564,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
return loginUrlInfo; return loginUrlInfo;
} }
private void initAuthTokenType() {
Bundle extras = getIntent().getExtras();
mAuthTokenType = null;
if (extras != null) {
mAuthTokenType = extras.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
}
if (mAuthTokenType == null) {
if (mAccount != null) {
boolean oAuthRequired = mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2) != null;
boolean samlWebSsoRequired = mAccountMgr.getUserData
(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
mAuthTokenType = chooseAuthTokenType(oAuthRequired, samlWebSsoRequired);
} else {
boolean oAuthSupported = AUTH_ON.equals(getString(R.string.auth_method_oauth2));
boolean samlWebSsoSupported = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
mAuthTokenType = chooseAuthTokenType(oAuthSupported, samlWebSsoSupported);
}
}
}
private String chooseAuthTokenType(boolean oauth, boolean saml) {
if (saml) {
return samlTokenType;
} else if (oauth) {
return oauthTokenType;
} else {
return basicTokenType;
}
}
/** /**
* Configures elements in the user interface under direct control of the Activity. * Configures elements in the user interface under direct control of the Activity.
*/ */
@ -634,7 +574,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mPasswordInput = findViewById(R.id.account_password); mPasswordInput = findViewById(R.id.account_password);
mUsernameInput = findViewById(R.id.account_username); mUsernameInput = findViewById(R.id.account_username);
mAuthStatusView = findViewById(R.id.auth_status_text); mAuthStatusView = findViewById(R.id.auth_status_text);
mOAuth2Check = findViewById(R.id.oauth_onOff_check);
mServerStatusView = findViewById(R.id.server_status_text); mServerStatusView = findViewById(R.id.server_status_text);
mTestServerButton = findViewById(R.id.testServerButton); mTestServerButton = findViewById(R.id.testServerButton);
@ -650,35 +589,18 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
private void setupInstructionMessage() { private void setupInstructionMessage() {
String instructionsMessageText = calculateInstructionMessageText(mAction, mAuthTokenType);
TextView instructionsView = findViewById(R.id.instructions_message); TextView instructionsView = findViewById(R.id.instructions_message);
if (instructionsMessageText != null) { if (mAction == ACTION_UPDATE_EXPIRED_TOKEN) {
instructionsView.setVisibility(View.VISIBLE); instructionsView.setVisibility(View.VISIBLE);
String instructionsMessageText = getString(R.string.auth_expired_basic_auth_toast);
instructionsView.setText(instructionsMessageText); instructionsView.setText(instructionsMessageText);
} else { } else {
instructionsView.setVisibility(View.GONE); instructionsView.setVisibility(View.GONE);
} }
} }
@Nullable
private String calculateInstructionMessageText(byte action, String authTokenType) {
if (action == ACTION_UPDATE_EXPIRED_TOKEN) {
if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType(this)).equals(authTokenType)) {
return getString(R.string.auth_expired_oauth_token_toast);
} else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType(this))
.equals(authTokenType)) {
return getString(R.string.auth_expired_saml_sso_token_toast);
} else {
return getString(R.string.auth_expired_basic_auth_toast);
}
}
return null;
}
public void onTestServerConnectionClick(View v) { public void onTestServerConnectionClick(View v) {
checkOcServer(); checkOcServer();
} }
@ -776,31 +698,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
} }
}; };
// TODO find out if this is really necessary, or if it can done in a different way
findViewById(R.id.scroll).setOnTouchListener((view, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN &&
AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(
MainApp.getAccountType(getBaseContext())).equals(mAuthTokenType) &&
mHostUrlInput.hasFocus()) {
checkOcServer();
}
return false;
});
} }
} }
/** /**
* @param savedInstanceState Saved activity state, as in {{@link #onCreate(Bundle)} * @param savedInstanceState Saved activity state, as in {{@link #onCreate(Bundle)}
*/ */
private void initAuthorizationPreFragment(Bundle savedInstanceState) { private void initAuthorizationPreFragment(Bundle savedInstanceState) {
/// step 0 - get UI elements in layout /// step 0 - get UI elements in layout
mOAuth2Check = findViewById(R.id.oauth_onOff_check);
mOAuthAuthEndpointText = findViewById(R.id.oAuthEntryPoint_1);
mOAuthTokenEndpointText = findViewById(R.id.oAuthEntryPoint_2);
mUsernameInput = findViewById(R.id.account_username); mUsernameInput = findViewById(R.id.account_username);
mPasswordInput = findViewById(R.id.account_password); mPasswordInput = findViewById(R.id.account_password);
mAuthStatusView = findViewById(R.id.auth_status_text); mAuthStatusView = findViewById(R.id.auth_status_text);
@ -820,8 +726,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
/// step 2 - set properties of UI elements (text, visibility, enabled...) /// step 2 - set properties of UI elements (text, visibility, enabled...)
mOAuth2Check.setChecked(
AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType(this)).equals(mAuthTokenType));
if (presetUserName != null) { if (presetUserName != null) {
mUsernameInput.setText(presetUserName); mUsernameInput.setText(presetUserName);
} }
@ -833,7 +737,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
if (isPasswordExposed) { if (isPasswordExposed) {
showPassword(); showPassword();
} }
updateAuthenticationPreFragmentVisibility();
showAuthStatus(); showAuthStatus();
mOkButton.setEnabled(mServerIsValid); mOkButton.setEnabled(mServerIsValid);
@ -855,53 +758,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
/**
* Changes the visibility of input elements depending on
* the current authorization method.
*/
private void updateAuthenticationPreFragmentVisibility() {
if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType(this)).equals(mAuthTokenType)) {
// SAML-based web Single Sign On
mOAuth2Check.setVisibility(View.GONE);
mOAuthAuthEndpointText.setVisibility(View.GONE);
mOAuthTokenEndpointText.setVisibility(View.GONE);
mUsernameInput.setVisibility(View.GONE);
mPasswordInput.setVisibility(View.GONE);
} else {
if (mAction == ACTION_CREATE &&
AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {
mOAuth2Check.setVisibility(View.VISIBLE);
} else {
mOAuth2Check.setVisibility(View.GONE);
}
if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType(this)).equals(mAuthTokenType)) {
// OAuth 2 authorization
mOAuthAuthEndpointText.setVisibility(View.VISIBLE);
mOAuthTokenEndpointText.setVisibility(View.VISIBLE);
mUsernameInput.setVisibility(View.GONE);
mPasswordInput.setVisibility(View.GONE);
} else {
// basic HTTP authorization
mOAuthAuthEndpointText.setVisibility(View.GONE);
mOAuthTokenEndpointText.setVisibility(View.GONE);
mUsernameInput.setVisibility(View.VISIBLE);
mPasswordInput.setVisibility(View.VISIBLE);
}
}
}
/** /**
* Saves relevant state before {@link #onPause()} * Saves relevant state before {@link #onPause()}
* *
* Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag,
* intended to defer the processing of the redirection caught in
* {@link #onNewIntent(Intent)} until {@link #onResume()}
*
* See {@link super#onSaveInstanceState(Bundle)} * See {@link super#onSaveInstanceState(Bundle)}
*/ */
@Override @Override
@ -910,7 +769,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
/// global state /// global state
outState.putString(KEY_AUTH_TOKEN_TYPE, mAuthTokenType);
outState.putLong(KEY_WAITING_FOR_OP_ID, mWaitingForOpId); outState.putLong(KEY_WAITING_FOR_OP_ID, mWaitingForOpId);
if (!webViewLoginMethod) { if (!webViewLoginMethod) {
@ -966,14 +824,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
String username = savedInstanceState.getString(KEY_USERNAME); String username = savedInstanceState.getString(KEY_USERNAME);
String password = savedInstanceState.getString(KEY_PASSWORD); String password = savedInstanceState.getString(KEY_PASSWORD);
OwnCloudCredentials credentials = null; OwnCloudCredentials credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);
if (basicTokenType.equals(mAuthTokenType)) {
credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);
} else if (oauthTokenType.equals(mAuthTokenType)) {
credentials = OwnCloudCredentialsFactory.newBearerCredentials(mAuthToken);
}
accessRootFolder(credentials); accessRootFolder(credentials);
} }
} }
@ -994,9 +845,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
Uri data = intent.getData(); Uri data = intent.getData();
if (data != null && data.toString().startsWith(getString(R.string.oauth2_redirect_uri))) {
mNewCapturedUriFromOAuth2Redirection = data;
}
if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) { if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) {
parseAndLoginFromWebView(data.toString()); parseAndLoginFromWebView(data.toString());
@ -1024,10 +872,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mHostUrlInput.setOnFocusChangeListener(this); mHostUrlInput.setOnFocusChangeListener(this);
mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher); mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher);
if (mNewCapturedUriFromOAuth2Redirection != null) {
getOAuth2AccessTokenFromCapturedRedirection();
}
String dataString = getIntent().getDataString(); String dataString = getIntent().getDataString();
if (dataString != null) { if (dataString != null) {
try { try {
@ -1085,39 +929,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
super.onDestroy(); super.onDestroy();
} }
/**
* Parses the redirection with the response to the GET AUTHORIZATION request to the
* oAuth server and requests for the access token (GET ACCESS TOKEN)
*/
private void getOAuth2AccessTokenFromCapturedRedirection() {
/// Parse data from OAuth redirection
String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();
mNewCapturedUriFromOAuth2Redirection = null;
/// Showing the dialog with instructions for the user.
IndeterminateProgressDialog dialog =
IndeterminateProgressDialog.newInstance(R.string.auth_getting_authorization, true);
dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
/// GET ACCESS TOKEN to the oAuth server
Intent getServerInfoIntent = new Intent();
getServerInfoIntent.setAction(OperationsService.ACTION_OAUTH2_GET_ACCESS_TOKEN);
getServerInfoIntent.putExtra(
OperationsService.EXTRA_SERVER_URL,
mOAuthTokenEndpointText.getText().toString().trim());
getServerInfoIntent.putExtra(
OperationsService.EXTRA_OAUTH2_QUERY_PARAMETERS,
queryParameters);
if (mOperationsServiceBinder != null) {
//Log_OC.e(TAG, "getting access token..." );
mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent);
}
}
/** /**
* Handles the change of focus on the text inputs for the server URL and the password * Handles the change of focus on the text inputs for the server URL and the password
*/ */
@ -1289,14 +1100,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
return; return;
} }
if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType(this)).equals(mAuthTokenType)) { checkBasicAuthorization(null, null);
startOauthorization();
} else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType(this))
.equals(mAuthTokenType)) {
startSamlBasedFederatedSingleSignOnAuthorization();
} else {
checkBasicAuthorization(null, null);
}
} }
@ -1334,57 +1138,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mAsyncTask.execute(params); mAsyncTask.execute(params);
} }
/**
* Starts the OAuth 'grant type' flow to get an access token, with
* a GET AUTHORIZATION request to the BUILT-IN authorization server.
*/
private void startOauthorization() {
// be gentle with the user
mAuthStatusIcon = R.drawable.progress_small;
mAuthStatusText = getResources().getString(R.string.oauth_login_connection);
showAuthStatus();
// GET AUTHORIZATION request
Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());
Uri.Builder uriBuilder = uri.buildUpon();
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type)
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri)
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id)
);
uriBuilder.appendQueryParameter(
OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope)
);
uri = uriBuilder.build();
Log_OC.d(TAG, "Starting browser to view " + uri.toString());
Intent i = new Intent(Intent.ACTION_VIEW, uri);
DisplayUtils.startIntentIfAppAvailable(i, this, R.string.no_browser_available);
}
/**
* Starts the Web Single Sign On flow to get access to the root folder
* in the server.
*/
private void startSamlBasedFederatedSingleSignOnAuthorization() {
/// be gentle with the user
mAuthStatusIcon = R.drawable.progress_small;
mAuthStatusText = getResources().getString(R.string.auth_connecting_auth_server);
showAuthStatus();
/// Show SAML-based SSO web dialog
String targetUrl = mServerInfo.mBaseUrl
+ AuthenticatorUrlUtils.getWebdavPath(mServerInfo.mVersion, mAuthTokenType, this);
SamlWebViewDialog dialog = SamlWebViewDialog.newInstance(targetUrl, targetUrl);
dialog.show(getSupportFragmentManager(), SAML_DIALOG_TAG);
}
/** /**
* Callback method invoked when a RemoteOperation executed by this Activity finishes. * Callback method invoked when a RemoteOperation executed by this Activity finishes.
* *
@ -1392,20 +1145,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
*/ */
@Override @Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
if (operation instanceof GetServerInfoOperation) { if (operation instanceof GetServerInfoOperation) {
if (operation.hashCode() == mWaitingForOpId) { if (operation.hashCode() == mWaitingForOpId) {
onGetServerInfoFinish(result); onGetServerInfoFinish(result);
} // else nothing ; only the last check operation is considered; } // else nothing ; only the last check operation is considered;
// multiple can be started if the user amends a URL quickly // multiple can be started if the user amends a URL quickly
} else if (operation instanceof OAuth2GetAccessToken) {
onGetOAuthAccessTokenFinish(result);
} else if (operation instanceof GetRemoteUserInfoOperation) { } else if (operation instanceof GetRemoteUserInfoOperation) {
onGetUserNameFinish(result); onGetUserNameFinish(result);
} }
} }
private void onGetUserNameFinish(RemoteOperationResult result) { private void onGetUserNameFinish(RemoteOperationResult result) {
@ -1548,9 +1296,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
private boolean authSupported(AuthenticationMethod authMethod) { private boolean authSupported(AuthenticationMethod authMethod) {
return (basicTokenType.equals(mAuthTokenType) && AuthenticationMethod.BASIC_HTTP_AUTH.equals(authMethod)) || return AuthenticationMethod.BASIC_HTTP_AUTH.equals(authMethod);
(oauthTokenType.equals(mAuthTokenType) && AuthenticationMethod.BEARER_TOKEN.equals(authMethod)) ||
(samlTokenType.equals(mAuthTokenType) && AuthenticationMethod.SAML_WEB_SSO.equals(authMethod));
} }
/** /**
@ -1706,40 +1452,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mServerStatusText = getResources().getString(R.string.auth_can_not_auth_against_server); mServerStatusText = getResources().getString(R.string.auth_can_not_auth_against_server);
} }
/**
* Processes the result of the request for and access token send
* to an OAuth authorization server.
*
* @param result Result of the operation.
*/
private void onGetOAuthAccessTokenFinish(RemoteOperationResult result) {
mWaitingForOpId = Long.MAX_VALUE;
dismissDialog(WAIT_DIALOG_TAG);
if (result.isSuccess()) {
/// be gentle with the user
IndeterminateProgressDialog dialog =
IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, true);
dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
/// time to test the retrieved access token on the ownCloud server
@SuppressWarnings("unchecked")
Map<String, String> tokens = (Map<String, String>) (result.getData().get(0));
mAuthToken = tokens.get(OAuth2Constants.KEY_ACCESS_TOKEN);
Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);
/// validate token accessing to root folder / getting session
OwnCloudCredentials credentials = OwnCloudCredentialsFactory.newBearerCredentials(
mAuthToken);
accessRootFolder(credentials);
} else {
updateAuthStatusIconAndText(result);
showAuthStatus();
Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
}
}
/** /**
* Processes the result of the access check performed to try the user credentials. * Processes the result of the access check performed to try the user credentials.
* *
@ -1801,7 +1513,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
CustomEditText serverAddressField = findViewById(R.id.hostUrlInput); CustomEditText serverAddressField = findViewById(R.id.hostUrlInput);
serverAddressField.setText(mServerInfo.mBaseUrl); serverAddressField.setText(mServerInfo.mBaseUrl);
findViewById(R.id.oauth_onOff_check).setVisibility(View.GONE);
findViewById(R.id.server_status_text).setVisibility(View.GONE); findViewById(R.id.server_status_text).setVisibility(View.GONE);
mAuthStatusView = findViewById(R.id.auth_status_text); mAuthStatusView = findViewById(R.id.auth_status_text);
@ -1855,7 +1566,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
CustomEditText serverAddressField = findViewById(R.id.hostUrlInput); CustomEditText serverAddressField = findViewById(R.id.hostUrlInput);
serverAddressField.setText(mServerInfo.mBaseUrl); serverAddressField.setText(mServerInfo.mBaseUrl);
findViewById(R.id.oauth_onOff_check).setVisibility(View.GONE);
findViewById(R.id.server_status_text).setVisibility(View.GONE); findViewById(R.id.server_status_text).setVisibility(View.GONE);
mAuthStatusView = findViewById(R.id.auth_status_text); mAuthStatusView = findViewById(R.id.auth_status_text);
@ -1884,32 +1594,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
* the new credentials when needed. * the new credentials when needed.
*/ */
private void updateAccountAuthentication() throws AccountNotFoundException { private void updateAccountAuthentication() throws AccountNotFoundException {
String accountType = MainApp.getAccountType(this);
Bundle response = new Bundle(); Bundle response = new Bundle();
response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type); response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);
if (AccountTypeUtils.getAuthTokenTypeAccessToken(accountType).equals(mAuthTokenType)) { if (webViewLoginMethod) {
response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken); response.putString(AccountManager.KEY_AUTHTOKEN, webViewPassword);
// the next line is necessary, notifications are calling directly to the mAccountMgr.setPassword(mAccount, webViewPassword);
// AuthenticatorActivity to update, without AccountManager intervention
mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
} else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(accountType).equals(mAuthTokenType)) {
response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);
// the next line is necessary; by now, notifications are calling directly to the
// AuthenticatorActivity to update, without AccountManager intervention
mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
} else { } else {
if (!webViewLoginMethod) { response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString()); mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
} else {
response.putString(AccountManager.KEY_AUTHTOKEN, webViewPassword);
mAccountMgr.setPassword(mAccount, webViewPassword);
}
} }
// remove managed clients for this account to enforce creation with fresh credentials // remove managed clients for this account to enforce creation with fresh credentials
@ -1936,9 +1630,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
String accountType = MainApp.getAccountType(this); String accountType = MainApp.getAccountType(this);
// create and save new ownCloud account // create and save new ownCloud account
boolean isOAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(accountType).equals(mAuthTokenType);
boolean isSaml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(accountType).equals(mAuthTokenType);
String lastPermanentLocation = authResult.getLastPermanentLocation(); String lastPermanentLocation = authResult.getLastPermanentLocation();
if (lastPermanentLocation != null) { if (lastPermanentLocation != null) {
mServerInfo.mBaseUrl = AuthenticatorUrlUtils.trimWebdavSuffix(lastPermanentLocation); mServerInfo.mBaseUrl = AuthenticatorUrlUtils.trimWebdavSuffix(lastPermanentLocation);
@ -1951,9 +1642,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} else { } else {
username = webViewUser; username = webViewUser;
} }
if (isOAuth) {
username = "OAuth_user" + new SecureRandom().nextLong();
}
String accountName = com.owncloud.android.lib.common.accounts.AccountUtils.buildAccountName(uri, username); String accountName = com.owncloud.android.lib.common.accounts.AccountUtils.buildAccountName(uri, username);
Account newAccount = new Account(accountName, accountType); Account newAccount = new Account(accountName, accountType);
@ -1970,15 +1658,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} else { } else {
mAccount = newAccount; mAccount = newAccount;
if (isOAuth || isSaml) { if (webViewLoginMethod) {
// with external authorizations, the password is never input in the app mAccountMgr.addAccountExplicitly(mAccount, webViewPassword, null);
mAccountMgr.addAccountExplicitly(mAccount, EMPTY_STRING, null);
} else { } else {
if (!webViewLoginMethod) { mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);
mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);
} else {
mAccountMgr.addAccountExplicitly(mAccount, webViewPassword, null);
}
} }
/// add the new account as default in preferences, if there is none already /// add the new account as default in preferences, if there is none already
@ -1996,9 +1679,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType); intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType);
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
intent.putExtra(AccountManager.KEY_USERDATA, username); intent.putExtra(AccountManager.KEY_USERDATA, username);
if (isOAuth || isSaml) {
mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
}
/// add user data to the new account; TODO probably can be done in the last parameter /// add user data to the new account; TODO probably can be done in the last parameter
// addAccountExplicitly, or in KEY_USERDATA // addAccountExplicitly, or in KEY_USERDATA
mAccountMgr.setUserData(mAccount, Constants.KEY_OC_VERSION, mServerInfo.mVersion.getVersion()); mAccountMgr.setUserData(mAccount, Constants.KEY_OC_VERSION, mServerInfo.mVersion.getVersion());
@ -2023,12 +1704,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
Log_OC.w(TAG, "Couldn't get display name and user id for " + username); Log_OC.w(TAG, "Couldn't get display name and user id for " + username);
} }
if (isSaml) {
mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE");
} else if (isOAuth) {
mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2, "TRUE");
}
setAccountAuthenticatorResult(intent.getExtras()); setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent); setResult(RESULT_OK, intent);
@ -2125,25 +1800,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
mPasswordInput.setSelection(selectionStart, selectionEnd); mPasswordInput.setSelection(selectionStart, selectionEnd);
} }
/**
* Called when the checkbox for OAuth authorization is clicked.
*
* Hides or shows the input fields for user & password.
*
* @param view 'View password' 'button'
*/
public void onCheckClick(View view) {
CheckBox oAuth2Check = (CheckBox) view;
if (oAuth2Check.isChecked()) {
mAuthTokenType = oauthTokenType;
} else {
mAuthTokenType = basicTokenType;
}
updateAuthenticationPreFragmentVisibility();
}
/** /**
* Called when the 'action' button in an IME is pressed ('enter' in software keyboard). * Called when the 'action' button in an IME is pressed ('enter' in software keyboard).
* *
@ -2215,38 +1871,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
} }
@Override
public void onSsoFinished(String sessionCookie) {
if (sessionCookie != null && sessionCookie.length() > 0) {
Log_OC.d(TAG, "Successful SSO - time to save the account");
mAuthToken = sessionCookie;
getRemoteUserNameOperation(sessionCookie);
Fragment fd = getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG);
if (fd instanceof DialogFragment) {
Dialog d = ((DialogFragment) fd).getDialog();
if (d != null && d.isShowing()) {
d.dismiss();
}
}
} else {
// TODO - show fail
Log_OC.d(TAG, "SSO failed");
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType(this)).equals(mAuthTokenType) &&
mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {
checkOcServer();
}
return super.onTouchEvent(event);
}
/** /**
* Show untrusted cert dialog * Show untrusted cert dialog
*/ */
@ -2279,34 +1903,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
/**
* Called from SslValidatorDialog when a new server certificate was correctly saved.
*/
public void onSavedCertificate() {
Fragment fd = getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG);
if (fd == null) {
// if SAML dialog is not shown,
// the SslDialog was shown due to an SSL error in the server check
checkOcServer();
}
}
/**
* Called from SslValidatorDialog when a new server certificate could not be saved
* when the user requested it.
*/
@Override
public void onFailedSavingCertificate() {
dismissDialog(SAML_DIALOG_TAG);
DisplayUtils.showSnackMessage(this, R.string.ssl_validator_not_saved);
}
@Override
public void onCancelCertificate() {
dismissDialog(SAML_DIALOG_TAG);
}
private void doOnResumeAndBound() { private void doOnResumeAndBound() {
//Log_OC.e(TAG, "registering to listen for operation callbacks" ); //Log_OC.e(TAG, "registering to listen for operation callbacks" );
mOperationsServiceBinder.addOperationListener(this, mHandler); mOperationsServiceBinder.addOperationListener(this, mHandler);
@ -2335,7 +1931,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
} }
} }
/** /**
* Implements callback methods for service binding. * Implements callback methods for service binding.
*/ */
@ -2431,4 +2026,43 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
parseAndLoginFromWebView(result); parseAndLoginFromWebView(result);
} }
} }
/**
* Obtain the X509Certificate from SslError
*
* @param error SslError
* @return X509Certificate from error
*/
public static X509Certificate getX509CertificateFromError(SslError error) {
Bundle bundle = SslCertificate.saveState(error.getCertificate());
X509Certificate x509Certificate;
byte[] bytes = bundle.getByteArray("x509-certificate");
if (bytes == null) {
x509Certificate = null;
} else {
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
x509Certificate = (X509Certificate) cert;
} catch (CertificateException e) {
x509Certificate = null;
}
}
return x509Certificate;
}
/**
* Called from SslValidatorDialog when a new server certificate was correctly saved.
*/
public void onSavedCertificate() {
checkOcServer();
}
/**
* Called from SslValidatorDialog when a new server certificate could not be saved when the user requested it.
*/
@Override
public void onFailedSavingCertificate() {
DisplayUtils.showSnackMessage(this, R.string.ssl_validator_not_saved);
}
} }

View file

@ -23,8 +23,6 @@ package com.owncloud.android.authentication;
import android.content.Context; import android.content.Context;
import com.owncloud.android.MainApp;
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import java.util.Locale; import java.util.Locale;
@ -38,9 +36,6 @@ public final class AuthenticatorUrlUtils {
private static final String HTTPS_PROTOCOL = "https://"; private static final String HTTPS_PROTOCOL = "https://";
private static final String HTTP_PROTOCOL = "http://"; private static final String HTTP_PROTOCOL = "http://";
private static final String ODAV_PATH = "/remote.php/odav";
private static final String SAML_SSO_PATH = "/remote.php/webdav";
private AuthenticatorUrlUtils() { private AuthenticatorUrlUtils() {
} }
@ -55,18 +50,7 @@ public final class AuthenticatorUrlUtils {
* is unknown; versions prior to ownCloud 4 are not supported anymore * is unknown; versions prior to ownCloud 4 are not supported anymore
*/ */
public static String getWebdavPath(OwnCloudVersion version, String authTokenType, Context context) { public static String getWebdavPath(OwnCloudVersion version, String authTokenType, Context context) {
if (version != null) {
String accountType = MainApp.getAccountType(context);
if (AccountTypeUtils.getAuthTokenTypeAccessToken(accountType).equals(authTokenType)) {
return ODAV_PATH;
}
if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(accountType).equals(authTokenType)) {
return SAML_SSO_PATH;
}
return WEBDAV_PATH_4_0_AND_LATER; return WEBDAV_PATH_4_0_AND_LATER;
}
return null;
} }
public static String normalizeUrlSuffix(String url) { public static String normalizeUrlSuffix(String url) {
@ -99,7 +83,7 @@ public final class AuthenticatorUrlUtils {
public static String trimWebdavSuffix(String url) { public static String trimWebdavSuffix(String url) {
String trimmedUrl = url; String trimmedUrl = url;
while(trimmedUrl.endsWith("/")) { while (trimmedUrl.endsWith("/")) {
trimmedUrl = trimmedUrl.substring(0, url.length() - 1); trimmedUrl = trimmedUrl.substring(0, url.length() - 1);
} }
@ -107,13 +91,7 @@ public final class AuthenticatorUrlUtils {
if (pos >= 0) { if (pos >= 0) {
trimmedUrl = trimmedUrl.substring(0, pos); trimmedUrl = trimmedUrl.substring(0, pos);
} else {
pos = trimmedUrl.lastIndexOf(ODAV_PATH);
if (pos >= 0) {
trimmedUrl = trimmedUrl.substring(0, pos);
}
} }
return trimmedUrl; return trimmedUrl;
} }

View file

@ -1,55 +0,0 @@
/*
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.authentication;
/**
* Constant values for OAuth 2 protocol.
*
* Includes required and optional parameter NAMES used in the 'authorization code' grant type.
*/
public final class OAuth2Constants {
/// Parameters to send to the Authorization Endpoint
public static final String KEY_RESPONSE_TYPE = "response_type";
public static final String KEY_REDIRECT_URI = "redirect_uri";
public static final String KEY_CLIENT_ID = "client_id";
public static final String KEY_SCOPE = "scope";
public static final String KEY_STATE = "state";
/// Additional parameters to send to the Token Endpoint
public static final String KEY_GRANT_TYPE = "grant_type";
public static final String KEY_CODE = "code";
/// Parameters received in an OK response from the Token Endpoint
public static final String KEY_ACCESS_TOKEN = "access_token";
public static final String KEY_TOKEN_TYPE = "token_type";
public static final String KEY_EXPIRES_IN = "expires_in";
public static final String KEY_REFRESH_TOKEN = "refresh_token";
/// Parameters in an ERROR response
public static final String KEY_ERROR = "error";
public static final String KEY_ERROR_DESCRIPTION = "error_description";
public static final String KEY_ERROR_URI = "error_uri";
public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
private OAuth2Constants() {
// No instance
}
}

View file

@ -1,190 +0,0 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.authentication;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.owncloud.android.lib.common.network.NetworkUtils;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.ByteArrayInputStream;
import java.lang.ref.WeakReference;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
/**
* Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process
* running in the {@link WebView} that is attached to.
*
* Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
* authentication process.
*/
public class SsoWebViewClient extends WebViewClient {
private static final String TAG = SsoWebViewClient.class.getSimpleName();
private Context mContext;
private Handler mListenerHandler;
private WeakReference<SsoWebViewClientListener> mListenerRef;
private String mTargetUrl;
private String mLastReloadedUrlAtError;
public interface SsoWebViewClientListener {
void onSsoFinished(String sessionCookie);
}
public SsoWebViewClient (Context context, Handler listenerHandler, SsoWebViewClientListener listener) {
mContext = context;
mListenerHandler = listenerHandler;
mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
mTargetUrl = "fake://url.to.be.set";
mLastReloadedUrlAtError = null;
}
public String getTargetUrl() {
return mTargetUrl;
}
public void setTargetUrl(String targetUrl) {
mTargetUrl = targetUrl;
}
@Override
public void onPageStarted (WebView view, String url, Bitmap favicon) {
Log_OC.d(TAG, "onPageStarted : " + url);
view.clearCache(true);
super.onPageStarted(view, url, favicon);
}
@Override
public void onFormResubmission (WebView view, Message dontResend, Message resend) {
Log_OC.d(TAG, "onFormResubMission ");
// necessary to grant reload of last page when device orientation is changed after sending a form
resend.sendToTarget();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
@Override
public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
if (!failingUrl.equals(mLastReloadedUrlAtError)) {
view.reload();
mLastReloadedUrlAtError = failingUrl;
} else {
mLastReloadedUrlAtError = null;
super.onReceivedError(view, errorCode, description, failingUrl);
}
}
@Override
public void onPageFinished (WebView view, String url) {
Log_OC.d(TAG, "onPageFinished : " + url);
mLastReloadedUrlAtError = null;
if (url.startsWith(mTargetUrl)) {
view.setVisibility(View.GONE);
CookieManager cookieManager = CookieManager.getInstance();
final String cookies = cookieManager.getCookie(url);
//Log_OC.d(TAG, "Cookies: " + cookies);
if (mListenerHandler != null && mListenerRef != null) {
// this is good idea because onPageFinished is not running in the UI thread
mListenerHandler.post(() -> {
SsoWebViewClientListener listener = mListenerRef.get();
if (listener != null) {
// Send Cookies to the listener
listener.onSsoFinished(cookies);
}
});
}
}
}
@Override
public void onReceivedSslError (final WebView view, final SslErrorHandler handler, SslError error) {
Log_OC.e(TAG, "onReceivedSslError : " + error);
// Test 1
X509Certificate x509Certificate = getX509CertificateFromError(error);
boolean isKnownServer = false;
if (x509Certificate != null) {
try {
isKnownServer = NetworkUtils.isCertInKnownServersStore(x509Certificate, mContext);
} catch (Exception e) {
Log_OC.e(TAG, "Exception: " + e.getMessage());
}
}
if (isKnownServer) {
handler.proceed();
} else {
((AuthenticatorActivity)mContext).showUntrustedCertDialog(x509Certificate, error, handler);
}
}
/**
* Obtain the X509Certificate from SslError
* @param error SslError
* @return X509Certificate from error
*/
public static X509Certificate getX509CertificateFromError(SslError error) {
Bundle bundle = SslCertificate.saveState(error.getCertificate());
X509Certificate x509Certificate;
byte[] bytes = bundle.getByteArray("x509-certificate");
if (bytes == null) {
x509Certificate = null;
} else {
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
x509Certificate = (X509Certificate) cert;
} catch (CertificateException e) {
x509Certificate = null;
}
}
return x509Certificate;
}
@Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
((AuthenticatorActivity)mContext).createAuthenticationDialog(view, handler);
}
}

View file

@ -1,4 +1,4 @@
/** /*
* ownCloud Android client application * ownCloud Android client application
* *
* Copyright (C) 2012 Bartek Przybylski * Copyright (C) 2012 Bartek Przybylski
@ -450,7 +450,7 @@ public class FileDownloader extends Service
/// perform the download /// perform the download
downloadResult = mCurrentDownload.execute(mDownloadClient, mCurrentDownload.getFile().isEncrypted()); downloadResult = mCurrentDownload.execute(mDownloadClient);
if (downloadResult.isSuccess()) { if (downloadResult.isSuccess()) {
saveDownloadedFile(); saveDownloadedFile();
} }
@ -460,11 +460,8 @@ public class FileDownloader extends Service
downloadResult = new RemoteOperationResult(e); downloadResult = new RemoteOperationResult(e);
} finally { } finally {
Pair<DownloadFileOperation, String> removeResult = Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.removePayload(
mPendingDownloads.removePayload( mCurrentAccount.name, mCurrentDownload.getRemotePath());
mCurrentAccount.name,
mCurrentDownload.getRemotePath()
);
/// notify result /// notify result
notifyDownloadResult(mCurrentDownload, downloadResult); notifyDownloadResult(mCurrentDownload, downloadResult);

View file

@ -56,7 +56,7 @@ public class CommentFileOperation extends SyncOperation {
*/ */
@Override @Override
protected RemoteOperationResult run(OwnCloudClient client) { protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = new CommentFileRemoteOperation(message, fileId, userId).execute(client, true); RemoteOperationResult result = new CommentFileRemoteOperation(message, fileId, userId).execute(client);
if (!result.isSuccess()) { if (!result.isSuccess()) {
Log_OC.e(this, "File with Id " + fileId + " could not be commented"); Log_OC.e(this, "File with Id " + fileId + " could not be commented");

View file

@ -65,7 +65,7 @@ public class CreateFolderOperation extends SyncOperation implements OnRemoteOper
if (result.isSuccess()) { if (result.isSuccess()) {
RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(mRemotePath) RemoteOperationResult remoteFolderOperationResult = new ReadFolderRemoteOperation(mRemotePath)
.execute(client, true); .execute(client);
createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0); createdRemoteFolder = (RemoteFile) remoteFolderOperationResult.getData().get(0);
saveFolderInDB(); saveFolderInDB();

View file

@ -158,7 +158,7 @@ public class DownloadFileOperation extends RemoteOperation {
while (listener.hasNext()) { while (listener.hasNext()) {
downloadOperation.addDatatransferProgressListener(listener.next()); downloadOperation.addDatatransferProgressListener(listener.next());
} }
result = downloadOperation.execute(client, client.isUseNextcloudUserAgent()); result = downloadOperation.execute(client);
if (result.isSuccess()) { if (result.isSuccess()) {
modificationTimestamp = downloadOperation.getModificationTimestamp(); modificationTimestamp = downloadOperation.getModificationTimestamp();

View file

@ -1,197 +0,0 @@
/*
* ownCloud Android client application
*
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.operations;
import com.owncloud.android.authentication.OAuth2Constants;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class OAuth2GetAccessToken extends RemoteOperation {
private static final String TAG = OAuth2GetAccessToken.class.getSimpleName();
private static final int KEY_INDEX = 0;
private static final int VALUE_INDEX = 1;
private String mClientId;
private String mRedirectUri;
private String mGrantType;
private String mOAuth2AuthorizationResponse;
private Map<String, String> mOAuth2ParsedAuthorizationResponse;
private Map<String, String> mResultTokenMap;
public OAuth2GetAccessToken(String clientId, String redirectUri, String grantType, String oAuth2AuthorizationResponse) {
mClientId = clientId;
mRedirectUri = redirectUri;
mGrantType = grantType;
mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
mOAuth2ParsedAuthorizationResponse = new HashMap<>();
mResultTokenMap = null;
}
/*
public Map<String, String> getResultTokenMap() {
return mResultTokenMap;
}
*/
@Override
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
PostMethod postMethod = null;
try {
parseAuthorizationResponse();
if (mOAuth2ParsedAuthorizationResponse.keySet().contains(OAuth2Constants.KEY_ERROR)) {
if (OAuth2Constants.VALUE_ERROR_ACCESS_DENIED.equals(mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_ERROR))) {
result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR_ACCESS_DENIED);
} else {
result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
}
}
if (result == null) {
NameValuePair[] nameValuePairs = new NameValuePair[4];
nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType);
nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_CODE));
nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri);
nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
//nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));
postMethod = new PostMethod(client.getWebdavUri().toString());
postMethod.setRequestBody(nameValuePairs);
client.executeMethod(postMethod);
String response = postMethod.getResponseBodyAsString();
if (response != null && response.length() > 0) {
JSONObject tokenJson = new JSONObject(response);
parseAccessTokenResult(tokenJson);
if (mResultTokenMap.get(OAuth2Constants.KEY_ERROR) != null || mResultTokenMap.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
} else {
result = new RemoteOperationResult(true, postMethod);
ArrayList<Object> data = new ArrayList<>();
data.add(mResultTokenMap);
result.setData(data);
}
} else {
result = new RemoteOperationResult(false, postMethod);
client.exhaustResponse(postMethod.getResponseBodyAsStream());
}
}
} catch (Exception e) {
result = new RemoteOperationResult(e);
} finally {
if (postMethod != null) {
postMethod.releaseConnection(); // let the connection available for other methods
}
final String code = "code";
final String oauth_token_request = "OAuth2 TOKEN REQUEST with auth code ";
if (result != null) {
if (result.isSuccess()) {
Log_OC.i(TAG, oauth_token_request + mOAuth2ParsedAuthorizationResponse.get(code) + " to " + client.getWebdavUri() + ": " + result.getLogMessage());
} else if (result.getException() != null) {
Log_OC.e(TAG, oauth_token_request + mOAuth2ParsedAuthorizationResponse.get(code) + " to " + client.getWebdavUri() + ": " + result.getLogMessage(), result.getException());
} else if (result.getCode() == ResultCode.OAUTH2_ERROR) {
Log_OC.e(TAG, oauth_token_request + mOAuth2ParsedAuthorizationResponse.get(code) + " to " + client.getWebdavUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
} else {
Log_OC.e(TAG, oauth_token_request + mOAuth2ParsedAuthorizationResponse.get(code) + " to " + client.getWebdavUri() + ": " + result.getLogMessage());
}
}
}
return result;
}
private void parseAuthorizationResponse() {
String[] pairs = mOAuth2AuthorizationResponse.split("&");
int i = 0;
String key = "";
String value;
while (pairs.length > i) {
int j = 0;
String[] part = pairs[i].split("=");
while (part.length > j) {
String p = part[j];
if (j == KEY_INDEX) {
key = p;
} else if (j == VALUE_INDEX) {
value = p;
mOAuth2ParsedAuthorizationResponse.put(key, value);
}
Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
j++;
}
i++;
}
}
private void parseAccessTokenResult (JSONObject tokenJson) throws JSONException {
mResultTokenMap = new HashMap<>();
if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
mResultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Constants.KEY_ACCESS_TOKEN));
}
if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
mResultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Constants.KEY_TOKEN_TYPE));
}
if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
mResultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Constants.KEY_EXPIRES_IN));
}
if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
mResultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Constants.KEY_REFRESH_TOKEN));
}
if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
mResultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.getString(OAuth2Constants.KEY_SCOPE));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
mResultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.getString(OAuth2Constants.KEY_ERROR));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
mResultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
}
if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
mResultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.getString(OAuth2Constants.KEY_ERROR_URI));
}
}
}

View file

@ -290,7 +290,7 @@ public class RefreshFolderOperation extends RemoteOperation {
Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
// remote request // remote request
result = new ReadFileRemoteOperation(remotePath).execute(client, true); result = new ReadFileRemoteOperation(remotePath).execute(client);
if (result.isSuccess()) { if (result.isSuccess()) {
OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0)); OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
@ -332,7 +332,7 @@ public class RefreshFolderOperation extends RemoteOperation {
private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) { private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) {
String remotePath = mLocalFolder.getRemotePath(); String remotePath = mLocalFolder.getRemotePath();
RemoteOperationResult result = new ReadFolderRemoteOperation(remotePath).execute(client, true); RemoteOperationResult result = new ReadFolderRemoteOperation(remotePath).execute(client);
Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath); Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
if (result.isSuccess()) { if (result.isSuccess()) {

View file

@ -94,7 +94,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
try { try {
// Lock folder // Lock folder
RemoteOperationResult lockFileOperationResult = new LockFileRemoteOperation(parentId).execute(client, true); RemoteOperationResult lockFileOperationResult = new LockFileRemoteOperation(parentId).execute(client);
if (lockFileOperationResult.isSuccess()) { if (lockFileOperationResult.isSuccess()) {
token = (String) lockFileOperationResult.getData().get(0); token = (String) lockFileOperationResult.getData().get(0);
@ -105,8 +105,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
} }
// refresh metadata // refresh metadata
RemoteOperationResult getMetadataOperationResult = new GetMetadataRemoteOperation(parentId) RemoteOperationResult getMetadataOperationResult = new GetMetadataRemoteOperation(parentId).execute(client);
.execute(client, true);
if (getMetadataOperationResult.isSuccess()) { if (getMetadataOperationResult.isSuccess()) {
// decrypt metadata // decrypt metadata
@ -138,7 +137,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
// upload metadata // upload metadata
RemoteOperationResult uploadMetadataOperationResult = new UpdateMetadataRemoteOperation(parentId, RemoteOperationResult uploadMetadataOperationResult = new UpdateMetadataRemoteOperation(parentId,
serializedFolderMetadata, token).execute(client, true); serializedFolderMetadata, token).execute(client);
if (!uploadMetadataOperationResult.isSuccess()) { if (!uploadMetadataOperationResult.isSuccess()) {
throw new RemoteOperationFailedException("Metadata not uploaded!"); throw new RemoteOperationFailedException("Metadata not uploaded!");
@ -158,7 +157,7 @@ public class RemoveRemoteEncryptedFileOperation extends RemoteOperation {
// unlock file // unlock file
if (token != null) { if (token != null) {
RemoteOperationResult unlockFileOperationResult = new UnlockFileRemoteOperation(parentId, token) RemoteOperationResult unlockFileOperationResult = new UnlockFileRemoteOperation(parentId, token)
.execute(client, true); .execute(client);
if (!unlockFileOperationResult.isSuccess()) { if (!unlockFileOperationResult.isSuccess()) {
Log_OC.e(TAG, "Failed to unlock " + parentId); Log_OC.e(TAG, "Failed to unlock " + parentId);

View file

@ -397,7 +397,7 @@ public class UploadFileOperation extends SyncOperation {
if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) {
UnlockFileRemoteOperation unlockFileOperation = new UnlockFileRemoteOperation(parent.getLocalId(), UnlockFileRemoteOperation unlockFileOperation = new UnlockFileRemoteOperation(parent.getLocalId(),
mFolderUnlockToken); mFolderUnlockToken);
RemoteOperationResult unlockFileOperationResult = unlockFileOperation.execute(client, true); RemoteOperationResult unlockFileOperationResult = unlockFileOperation.execute(client);
if (!unlockFileOperationResult.isSuccess()) { if (!unlockFileOperationResult.isSuccess()) {
return unlockFileOperationResult; return unlockFileOperationResult;
@ -451,7 +451,7 @@ public class UploadFileOperation extends SyncOperation {
// Lock folder // Lock folder
LockFileRemoteOperation lockFileOperation = new LockFileRemoteOperation(parentFile.getLocalId()); LockFileRemoteOperation lockFileOperation = new LockFileRemoteOperation(parentFile.getLocalId());
RemoteOperationResult lockFileOperationResult = lockFileOperation.execute(client, true); RemoteOperationResult lockFileOperationResult = lockFileOperation.execute(client);
if (lockFileOperationResult.isSuccess()) { if (lockFileOperationResult.isSuccess()) {
token = (String) lockFileOperationResult.getData().get(0); token = (String) lockFileOperationResult.getData().get(0);
@ -466,7 +466,7 @@ public class UploadFileOperation extends SyncOperation {
// Update metadata // Update metadata
GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId()); GetMetadataRemoteOperation getMetadataOperation = new GetMetadataRemoteOperation(parentFile.getLocalId());
RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client, true); RemoteOperationResult getMetadataOperationResult = getMetadataOperation.execute(client);
DecryptedFolderMetadata metadata; DecryptedFolderMetadata metadata;
@ -604,7 +604,7 @@ public class UploadFileOperation extends SyncOperation {
throw new OperationCancelledException(); throw new OperationCancelledException();
} }
result = mUploadOperation.execute(client, true); result = mUploadOperation.execute(client);
/// move local temporal file or original file to its corresponding /// move local temporal file or original file to its corresponding
// location in the Nextcloud local folder // location in the Nextcloud local folder
@ -636,12 +636,12 @@ public class UploadFileOperation extends SyncOperation {
// update metadata // update metadata
UpdateMetadataRemoteOperation storeMetadataOperation = new UpdateMetadataRemoteOperation( UpdateMetadataRemoteOperation storeMetadataOperation = new UpdateMetadataRemoteOperation(
parentFile.getLocalId(), serializedFolderMetadata, token); parentFile.getLocalId(), serializedFolderMetadata, token);
uploadMetadataOperationResult = storeMetadataOperation.execute(client, true); uploadMetadataOperationResult = storeMetadataOperation.execute(client);
} else { } else {
// store metadata // store metadata
StoreMetadataRemoteOperation storeMetadataOperation = new StoreMetadataRemoteOperation( StoreMetadataRemoteOperation storeMetadataOperation = new StoreMetadataRemoteOperation(
parentFile.getLocalId(), serializedFolderMetadata); parentFile.getLocalId(), serializedFolderMetadata);
uploadMetadataOperationResult = storeMetadataOperation.execute(client, true); uploadMetadataOperationResult = storeMetadataOperation.execute(client);
} }
if (!uploadMetadataOperationResult.isSuccess()) { if (!uploadMetadataOperationResult.isSuccess()) {
@ -715,7 +715,7 @@ public class UploadFileOperation extends SyncOperation {
private RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudClient client, String token) { private RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudClient client, String token) {
if (token != null) { if (token != null) {
return new UnlockFileRemoteOperation(parentFolder.getLocalId(), token).execute(client, true); return new UnlockFileRemoteOperation(parentFolder.getLocalId(), token).execute(client);
} else { } else {
return new RemoteOperationResult(new Exception("No token available")); return new RemoteOperationResult(new Exception("No token available"));
} }
@ -853,7 +853,7 @@ public class UploadFileOperation extends SyncOperation {
} }
if (result.isSuccess() && mUploadOperation != null) { if (result.isSuccess() && mUploadOperation != null) {
result = mUploadOperation.execute(client, mFile.isEncrypted()); result = mUploadOperation.execute(client);
/// move local temporal file or original file to its corresponding /// move local temporal file or original file to its corresponding
// location in the Nextcloud local folder // location in the Nextcloud local folder
@ -1017,7 +1017,7 @@ public class UploadFileOperation extends SyncOperation {
*/ */
private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) { private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) {
RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, false); RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, false);
RemoteOperationResult result = operation.execute(client, true); RemoteOperationResult result = operation.execute(client);
if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) { if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true); SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
result = syncOp.execute(client, getStorageManager()); result = syncOp.execute(client, getStorageManager());
@ -1318,7 +1318,7 @@ public class UploadFileOperation extends SyncOperation {
} }
ReadFileRemoteOperation operation = new ReadFileRemoteOperation(path); ReadFileRemoteOperation operation = new ReadFileRemoteOperation(path);
RemoteOperationResult result = operation.execute(client, mFile.isEncrypted()); RemoteOperationResult result = operation.execute(client);
if (result.isSuccess()) { if (result.isSuccess()) {
updateOCFile(file, (RemoteFile) result.getData().get(0)); updateOCFile(file, (RemoteFile) result.getData().get(0));
file.setLastSyncDateForProperties(syncDate); file.setLastSyncDateForProperties(syncDate);

View file

@ -39,7 +39,6 @@ import android.os.Process;
import android.util.Pair; import android.util.Pair;
import com.owncloud.android.MainApp; import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudAccount;
@ -64,7 +63,6 @@ import com.owncloud.android.operations.CreateShareViaLinkOperation;
import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.CreateShareWithShareeOperation;
import com.owncloud.android.operations.GetServerInfoOperation; import com.owncloud.android.operations.GetServerInfoOperation;
import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.MoveFileOperation;
import com.owncloud.android.operations.OAuth2GetAccessToken;
import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation;
@ -117,9 +115,7 @@ public class OperationsService extends Service {
public static final String ACTION_UPDATE_SHARE = "UPDATE_SHARE"; public static final String ACTION_UPDATE_SHARE = "UPDATE_SHARE";
public static final String ACTION_UPDATE_SHARE_NOTE = "UPDATE_SHARE_NOTE"; public static final String ACTION_UPDATE_SHARE_NOTE = "UPDATE_SHARE_NOTE";
public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO"; public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
public static final String ACTION_OAUTH2_GET_ACCESS_TOKEN = "OAUTH2_GET_ACCESS_TOKEN";
public static final String ACTION_GET_USER_NAME = "GET_USER_NAME"; public static final String ACTION_GET_USER_NAME = "GET_USER_NAME";
public static final String ACTION_GET_USER_AVATAR = "GET_USER_AVATAR";
public static final String ACTION_RENAME = "RENAME"; public static final String ACTION_RENAME = "RENAME";
public static final String ACTION_REMOVE = "REMOVE"; public static final String ACTION_REMOVE = "REMOVE";
public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER"; public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
@ -640,13 +636,6 @@ public class OperationsService extends Service {
operation = new GetServerInfoOperation(serverUrl, this); operation = new GetServerInfoOperation(serverUrl, this);
break; break;
case ACTION_OAUTH2_GET_ACCESS_TOKEN:
String oauth2QueryParameters = operationIntent.getStringExtra(EXTRA_OAUTH2_QUERY_PARAMETERS);
operation = new OAuth2GetAccessToken(getString(R.string.oauth2_client_id),
getString(R.string.oauth2_redirect_uri), getString(R.string.oauth2_grant_type),
oauth2QueryParameters);
break;
case ACTION_GET_USER_NAME: case ACTION_GET_USER_NAME:
operation = new GetRemoteUserInfoOperation(); operation = new GetRemoteUserInfoOperation();
break; break;

View file

@ -605,11 +605,6 @@ public abstract class FileActivity extends DrawerActivity
dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED); dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED);
} }
@Override
public void onCancelCertificate() {
// nothing to do
}
public void checkForNewDevVersionNecessary(View view, Context context) { public void checkForNewDevVersionNecessary(View view, Context context) {
if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) { if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver()); ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());

View file

@ -1,269 +0,0 @@
/**
* ownCloud Android client application
*
* @author Maria Asensio
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.ui.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.RelativeLayout;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.SsoWebViewClient;
import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
import com.owncloud.android.lib.common.utils.Log_OC;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
/**
* Dialog to show the WebView for SAML Authentication
*/
public class SamlWebViewDialog extends DialogFragment {
private final static String TAG = SamlWebViewDialog.class.getSimpleName();
private static final String ARG_INITIAL_URL = "INITIAL_URL";
private static final String ARG_TARGET_URL = "TARGET_URL";
private WebView mSsoWebView;
private SsoWebViewClient mWebViewClient;
private String mInitialUrl;
private String mTargetUrl;
private SsoWebViewClientListener mSsoWebViewClientListener;
/**
* Public factory method to get dialog instances.
*
* @param url Url to open at WebView
* @param targetUrl mBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, m
* CurrentAuthTokenType)
* @return New dialog instance, ready to show.
*/
public static SamlWebViewDialog newInstance(String url, String targetUrl) {
SamlWebViewDialog fragment = new SamlWebViewDialog();
Bundle args = new Bundle();
args.putString(ARG_INITIAL_URL, url);
args.putString(ARG_TARGET_URL, targetUrl);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Activity activity) {
Log_OC.v(TAG, "onAttach");
super.onAttach(activity);
try {
mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
Handler mHandler = new Handler();
mWebViewClient = new SsoWebViewClient(activity, mHandler, mSsoWebViewClientListener);
} catch (ClassCastException e) {
throw new IllegalArgumentException(activity.toString() + " must implement " +
SsoWebViewClientListener.class.getSimpleName(), e);
}
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
Log_OC.v(TAG, "onCreate, savedInstanceState is " + savedInstanceState);
super.onCreate(savedInstanceState);
setRetainInstance(true);
CookieSyncManager.createInstance(getActivity().getApplicationContext());
if (savedInstanceState == null) {
mInitialUrl = getArguments().getString(ARG_INITIAL_URL);
mTargetUrl = getArguments().getString(ARG_TARGET_URL);
} else {
mInitialUrl = savedInstanceState.getString(ARG_INITIAL_URL);
mTargetUrl = savedInstanceState.getString(ARG_TARGET_URL);
}
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.Theme_ownCloud_Dialog);
}
@SuppressWarnings("deprecation")
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log_OC.v(TAG, "onCreateView, savedInsanceState is " + savedInstanceState);
// Inflate layout of the dialog
RelativeLayout ssoRootView = (RelativeLayout) inflater.inflate(R.layout.sso_dialog,
container, false); // null parent view because it will go in the dialog layout
if (mSsoWebView == null) {
// initialize the WebView
mSsoWebView = new SsoWebView(getActivity().getApplicationContext());
mSsoWebView.setFocusable(true);
mSsoWebView.setFocusableInTouchMode(true);
mSsoWebView.setClickable(true);
WebSettings webSettings = mSsoWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setSavePassword(false);
webSettings.setUserAgentString(MainApp.getUserAgent());
webSettings.setSaveFormData(false);
// next two settings grant that non-responsive webs are zoomed out when loaded
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
// next three settings allow the user use pinch gesture to zoom in / out
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
webSettings.setAllowFileAccess(false);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeAllCookie();
mSsoWebView.loadUrl(mInitialUrl);
}
mWebViewClient.setTargetUrl(mTargetUrl);
mSsoWebView.setWebViewClient(mWebViewClient);
// add the webview into the layout
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
ssoRootView.addView(mSsoWebView, layoutParams);
ssoRootView.requestLayout();
return ssoRootView;
}
@Override
public void onSaveInstanceState(Bundle outState) {
Log_OC.v(TAG, "onSaveInstanceState being CALLED");
super.onSaveInstanceState(outState);
// save URLs
outState.putString(ARG_INITIAL_URL, mInitialUrl);
outState.putString(ARG_TARGET_URL, mTargetUrl);
}
@Override
public void onDestroyView() {
Log_OC.v(TAG, "onDestroyView");
if (mSsoWebView.getParent() != null) {
((ViewGroup)mSsoWebView.getParent()).removeView(mSsoWebView);
}
mSsoWebView.setWebViewClient(null);
// Work around bug: http://code.google.com/p/android/issues/detail?id=17423
Dialog dialog = getDialog();
if (dialog != null) {
dialog.setOnDismissListener(null);
}
super.onDestroyView();
}
@Override
public void onDestroy() {
Log_OC.v(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void onDetach() {
Log_OC.v(TAG, "onDetach");
mSsoWebViewClientListener = null;
mWebViewClient = null;
super.onDetach();
}
@Override
public void onCancel (DialogInterface dialog) {
Log_OC.d(TAG, "onCancel");
super.onCancel(dialog);
}
@Override
public void onDismiss (DialogInterface dialog) {
Log_OC.d(TAG, "onDismiss");
super.onDismiss(dialog);
}
@Override
public void onStart() {
Log_OC.v(TAG, "onStart");
super.onStart();
}
@Override
public void onStop() {
Log_OC.v(TAG, "onStop");
super.onStop();
}
@Override
public void onResume() {
Log_OC.v(TAG, "onResume");
super.onResume();
mSsoWebView.onResume();
}
@Override
public void onPause() {
Log_OC.v(TAG, "onPause");
mSsoWebView.onPause();
super.onPause();
}
@Override
public int show (FragmentTransaction transaction, String tag) {
Log_OC.v(TAG, "show (transaction)");
return super.show(transaction, tag);
}
@Override
public void show (FragmentManager manager, String tag) {
Log_OC.v(TAG, "show (manager)");
super.show(manager, tag);
}
}

View file

@ -256,8 +256,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
// get user id // get user id
String userID; String userID;
GetRemoteUserInfoOperation remoteUserNameOperation = new GetRemoteUserInfoOperation(); GetRemoteUserInfoOperation remoteUserNameOperation = new GetRemoteUserInfoOperation();
RemoteOperationResult remoteUserNameOperationResult = remoteUserNameOperation.execute(account, RemoteOperationResult remoteUserNameOperationResult = remoteUserNameOperation.execute(account, getContext());
getContext(), true);
if (remoteUserNameOperationResult.isSuccess() && remoteUserNameOperationResult.getData() != null) { if (remoteUserNameOperationResult.isSuccess() && remoteUserNameOperationResult.getData() != null) {
UserInfo userInfo = (UserInfo) remoteUserNameOperationResult.getData().get(0); UserInfo userInfo = (UserInfo) remoteUserNameOperationResult.getData().get(0);
@ -267,7 +266,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
} }
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation(userID); GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation(userID);
RemoteOperationResult publicKeyResult = publicKeyOperation.execute(account, getContext(), true); RemoteOperationResult publicKeyResult = publicKeyOperation.execute(account, getContext());
if (publicKeyResult.isSuccess()) { if (publicKeyResult.isSuccess()) {
Log_OC.d(TAG, "public key successful downloaded for " + account.name); Log_OC.d(TAG, "public key successful downloaded for " + account.name);
@ -280,7 +279,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
} }
GetPrivateKeyOperation privateKeyOperation = new GetPrivateKeyOperation(); GetPrivateKeyOperation privateKeyOperation = new GetPrivateKeyOperation();
RemoteOperationResult privateKeyResult = privateKeyOperation.execute(account, getContext(), true); RemoteOperationResult privateKeyResult = privateKeyOperation.execute(account, getContext());
if (privateKeyResult.isSuccess()) { if (privateKeyResult.isSuccess()) {
Log_OC.d(TAG, "private key successful downloaded for " + account.name); Log_OC.d(TAG, "private key successful downloaded for " + account.name);
@ -351,8 +350,8 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
// get user id // get user id
String userID; String userID;
GetRemoteUserInfoOperation remoteUserNameOperation = new GetRemoteUserInfoOperation(); GetRemoteUserInfoOperation remoteUserNameOperation = new GetRemoteUserInfoOperation();
RemoteOperationResult remoteUserNameOperationResult = remoteUserNameOperation RemoteOperationResult remoteUserNameOperationResult = remoteUserNameOperation.execute(account,
.execute(account, getContext(), true); getContext());
if (remoteUserNameOperationResult.isSuccess() && if (remoteUserNameOperationResult.isSuccess() &&
remoteUserNameOperationResult.getData() != null) { remoteUserNameOperationResult.getData() != null) {
@ -366,7 +365,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userID); String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userID);
SendCSROperation operation = new SendCSROperation(urlEncoded); SendCSROperation operation = new SendCSROperation(urlEncoded);
RemoteOperationResult result = operation.execute(account, getContext(), true); RemoteOperationResult result = operation.execute(account, getContext());
if (result.isSuccess()) { if (result.isSuccess()) {
Log_OC.d(TAG, "public key success"); Log_OC.d(TAG, "public key success");
@ -384,8 +383,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
// upload encryptedPrivateKey // upload encryptedPrivateKey
StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey); StorePrivateKeyOperation storePrivateKeyOperation = new StorePrivateKeyOperation(encryptedPrivateKey);
RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(account, getContext(), RemoteOperationResult storePrivateKeyResult = storePrivateKeyOperation.execute(account, getContext());
true);
if (storePrivateKeyResult.isSuccess()) { if (storePrivateKeyResult.isSuccess()) {
Log_OC.d(TAG, "private key success"); Log_OC.d(TAG, "private key success");
@ -400,7 +398,7 @@ public class SetupEncryptionDialogFragment extends DialogFragment {
return (String) storePrivateKeyResult.getData().get(0); return (String) storePrivateKeyResult.getData().get(0);
} else { } else {
DeletePublicKeyOperation deletePublicKeyOperation = new DeletePublicKeyOperation(); DeletePublicKeyOperation deletePublicKeyOperation = new DeletePublicKeyOperation();
deletePublicKeyOperation.execute(account, getContext(), true); deletePublicKeyOperation.execute(account, getContext());
} }
} catch (Exception e) { } catch (Exception e) {
Log_OC.e(TAG, e.getMessage()); Log_OC.e(TAG, e.getMessage());

View file

@ -194,7 +194,6 @@ public class SslUntrustedCertDialog extends DialogFragment {
if (mHandler != null) { if (mHandler != null) {
mHandler.cancel(); mHandler.cancel();
} }
((OnSslUntrustedCertListener)getActivity()).onCancelCertificate();
} }
} }
@ -226,8 +225,6 @@ public class SslUntrustedCertDialog extends DialogFragment {
void onSavedCertificate(); void onSavedCertificate();
void onFailedSavingCertificate(); void onFailedSavingCertificate();
void onCancelCertificate();
} }
public interface ErrorViewAdapter { public interface ErrorViewAdapter {

View file

@ -1585,7 +1585,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
ToggleEncryptionRemoteOperation toggleEncryptionOperation = new ToggleEncryptionRemoteOperation( ToggleEncryptionRemoteOperation toggleEncryptionOperation = new ToggleEncryptionRemoteOperation(
event.localId, event.remotePath, event.shouldBeEncrypted); event.localId, event.remotePath, event.shouldBeEncrypted);
RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(mClient, true); RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(mClient);
if (remoteOperationResult.isSuccess()) { if (remoteOperationResult.isSuccess()) {
mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted); mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted);

View file

@ -219,7 +219,7 @@ public final class EncryptionUtils {
DecryptedFolderMetadata downloadFolderMetadata(OCFile folder, OwnCloudClient client, DecryptedFolderMetadata downloadFolderMetadata(OCFile folder, OwnCloudClient client,
Context context, Account account) { Context context, Account account) {
RemoteOperationResult getMetadataOperationResult = new GetMetadataRemoteOperation(folder.getLocalId()) RemoteOperationResult getMetadataOperationResult = new GetMetadataRemoteOperation(folder.getLocalId())
.execute(client, true); .execute(client);
if (!getMetadataOperationResult.isSuccess()) { if (!getMetadataOperationResult.isSuccess()) {
return null; return null;

View file

@ -158,62 +158,6 @@
android:minHeight="@dimen/display_text_min_height" android:minHeight="@dimen/display_text_min_height"
android:contentDescription="@string/auth_testing_connection"/> android:contentDescription="@string/auth_testing_connection"/>
<CheckBox
android:id="@+id/oauth_onOff_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:onClick="onCheckClick"
android:text="@string/oauth_check_onoff"
android:textColor="@color/primary"
android:textAppearance="?android:attr/textAppearanceSmall"
android:contentDescription="@string/oauth_check_onoff"
/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_oAuthEntryPoint_1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/oAuthEntryPoint_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:enabled="false"
android:hint="@string/oauth_2_0_auth_end_point_address_hint"
android:inputType="textUri"
android:maxLines="1"
android:text="@string/oauth2_url_endpoint_auth"
android:textColor="@color/login_text_color"
android:textColorHint="@color/login_text_hint_color"
android:importantForAutofill="no"
android:visibility="gone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_oAuthEntryPoint_2"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/oAuthEntryPoint_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:enabled="false"
android:hint="@string/oauth_2_0_access_end_point_address_hint"
android:inputType="textUri"
android:maxLines="1"
android:text="@string/oauth2_url_endpoint_access"
android:textColor="@color/login_text_color"
android:textColorHint="@color/login_text_hint_color"
android:importantForAutofill="no"
android:visibility="gone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_account_username" android:id="@+id/input_layout_account_username"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -151,62 +151,6 @@
android:minHeight="@dimen/display_text_min_height" android:minHeight="@dimen/display_text_min_height"
android:contentDescription="@string/auth_testing_connection"/> android:contentDescription="@string/auth_testing_connection"/>
<CheckBox
android:id="@+id/oauth_onOff_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:onClick="onCheckClick"
android:text="@string/oauth_check_onoff"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/login_text_hint_color"
android:contentDescription="@string/oauth_check_onoff"
/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_oAuthEntryPoint_1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/oAuthEntryPoint_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:enabled="false"
android:hint="@string/oauth_2_0_auth_end_point_address_hint"
android:inputType="textUri"
android:maxLines="1"
android:text="@string/oauth2_url_endpoint_auth"
android:textColor="@color/login_text_color"
android:textColorHint="@color/login_text_hint_color"
android:visibility="gone"
android:importantForAutofill="no"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_oAuthEntryPoint_2"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/oAuthEntryPoint_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:enabled="false"
android:hint="@string/oauth_2_0_access_end_point_address_hint"
android:inputType="textUri"
android:maxLines="1"
android:text="@string/oauth2_url_endpoint_access"
android:textColor="@color/login_text_color"
android:textColorHint="@color/login_text_hint_color"
android:importantForAutofill="no"
android:visibility="gone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_layout_account_username" android:id="@+id/input_layout_account_username"
android:theme="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:theme="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
Copyright (C) 2015 ownCloud Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2,
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</RelativeLayout>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- constants that must be respected by the authorization server; if changed, the app must be rebuild -->
<string name="oauth2_redirect_scheme">owncloud</string>
<string name="oauth2_redirect_uri">owncloud://callback</string>
<!-- values that should be provided by ownCloud server -->
<string name="oauth2_url_endpoint_auth">http://oauth2.authorization.server.org/path/to/endpoint/for/authorization</string>
<string name="oauth2_url_endpoint_access">http://oauth2.authorization.server.org/path/to/endpoint/for/access/token</string>
<string name="oauth2_scope">owncloud</string>
<string name="oauth2_grant_type">authorization_code</string> <!-- the only one supported right now -->
<string name="oauth2_response_type">code</string> <!-- depends on oauth2_grant_type -->
<!-- values that should be pre-agreed between app and authorization server, but can be loaded without rebuilding the app -->
<string name="oauth2_client_id">com.owncloud.android</string> <!-- preferable that client decides this -->
</resources>

View file

@ -30,10 +30,6 @@
<bool name="show_provider_or_own_installation">true</bool> <bool name="show_provider_or_own_installation">true</bool>
<string name="provider_registration_server">https://www.nextcloud.com/register</string> <string name="provider_registration_server">https://www.nextcloud.com/register</string>
<!-- Flags to setup the authentication methods available in the app -->
<string name="auth_method_oauth2">off</string>
<string name="auth_method_saml_web_sso">off</string>
<!-- Flags to enable/disable some features --> <!-- Flags to enable/disable some features -->
<string name="send_files_to_other_apps">on</string> <string name="send_files_to_other_apps">on</string>
<bool name="share_via_link_feature">true</bool> <bool name="share_via_link_feature">true</bool>

View file

@ -232,7 +232,6 @@
<string name="media_play_pause_description">Play or pause button</string> <string name="media_play_pause_description">Play or pause button</string>
<string name="media_forward_description">Fast forward button</string> <string name="media_forward_description">Fast forward button</string>
<string name="auth_getting_authorization">Getting authorization…</string>
<string name="auth_trying_to_login">Trying to log in…</string> <string name="auth_trying_to_login">Trying to log in…</string>
<string name="auth_no_net_conn_title">No network connection</string> <string name="auth_no_net_conn_title">No network connection</string>
<string name="auth_nossl_plain_ok_title">Secure connection unavailable.</string> <string name="auth_nossl_plain_ok_title">Secure connection unavailable.</string>
@ -257,10 +256,7 @@
<string name="auth_oauth_error">Unsuccessful authorization</string> <string name="auth_oauth_error">Unsuccessful authorization</string>
<string name="auth_oauth_error_access_denied">Access denied by authorization server</string> <string name="auth_oauth_error_access_denied">Access denied by authorization server</string>
<string name="auth_wtf_reenter_URL">Unexpected state, please enter the server address again</string> <string name="auth_wtf_reenter_URL">Unexpected state, please enter the server address again</string>
<string name="auth_expired_oauth_token_toast">Your authorization expired. Please, authorize again</string>
<string name="auth_expired_basic_auth_toast">Please enter the current password</string> <string name="auth_expired_basic_auth_toast">Please enter the current password</string>
<string name="auth_expired_saml_sso_token_toast">Your session expired. Please connect again</string>
<string name="auth_connecting_auth_server">Connecting to authentication server…</string>
<string name="auth_unsupported_multiaccount">%1$s does not support multiple accounts</string> <string name="auth_unsupported_multiaccount">%1$s does not support multiple accounts</string>
<string name="auth_fail_get_user_name">Your server is not returning a correct user ID, please contact an admin</string> <string name="auth_fail_get_user_name">Your server is not returning a correct user ID, please contact an admin</string>
<string name="auth_can_not_auth_against_server">Cannot authenticate to this server</string> <string name="auth_can_not_auth_against_server">Cannot authenticate to this server</string>
@ -292,10 +288,6 @@
<string name="filedisplay_no_file_selected">No file selected</string> <string name="filedisplay_no_file_selected">No file selected</string>
<string name="activity_chooser_title">Send link to…</string> <string name="activity_chooser_title">Send link to…</string>
<string name="wait_for_tmp_copy_from_private_storage">Copying file from private storage</string> <string name="wait_for_tmp_copy_from_private_storage">Copying file from private storage</string>
<string name="oauth_check_onoff">Log in with OAuth 2.0</string>
<string name="oauth_login_connection">Connecting to OAuth 2.0 server…</string>
<string name="ssl_validator_header">The identity of the server could not be verified</string> <string name="ssl_validator_header">The identity of the server could not be verified</string>
<string name="ssl_validator_reason_cert_not_trusted">- The server certificate is not trusted</string> <string name="ssl_validator_reason_cert_not_trusted">- The server certificate is not trusted</string>
<string name="ssl_validator_reason_cert_expired">- The server certificate expired</string> <string name="ssl_validator_reason_cert_expired">- The server certificate expired</string>
@ -751,8 +743,6 @@
<string name="notification_channel_push_description">Show push notifications sent by the server: Mentions in comments, reception of new remote shares, announcements posted by an admin etc.</string> <string name="notification_channel_push_description">Show push notifications sent by the server: Mentions in comments, reception of new remote shares, announcements posted by an admin etc.</string>
<string name="sendbutton_description">Send button icon</string> <string name="sendbutton_description">Send button icon</string>
<string name="oauth_2_0_auth_end_point_address_hint">Auth end point address</string>
<string name="oauth_2_0_access_end_point_address_hint">Access end point address</string>
<string name="hidden_character" translatable="false">*</string> <string name="hidden_character" translatable="false">*</string>
<string name="hint_name">Name</string> <string name="hint_name">Name</string>
<string name="hint_password">Password</string> <string name="hint_password">Password</string>