Loading app/src/main/java/at/bitfire/davdroid/ui/setup/EeloAuthenticatorFragment.kt +35 −326 Original line number Original line Diff line number Diff line package at.bitfire.davdroid.ui.setup package at.bitfire.davdroid.ui.setup import android.accounts.Account import android.accounts.AccountManager import android.app.Activity import android.app.PendingIntent import android.content.Context import android.content.Context import android.content.Intent import android.net.Uri import android.os.* import android.os.* import android.support.v4.app.Fragment import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.LayoutInflater import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import at.bitfire.dav4android.Constants import at.bitfire.davdroid.authorization.IdentityProvider import at.bitfire.davdroid.R import at.bitfire.davdroid.R import kotlinx.android.synthetic.main.login_credentials_fragment.view.* import net.openid.appauth.* import org.json.JSONException import org.json.JSONObject import java.io.BufferedReader import java.io.IOException import java.io.InputStream import java.io.InputStreamReader import java.net.* import java.util.HashMap import java.util.logging.Level import android.net.ConnectivityManager import android.net.ConnectivityManager import android.net.Uri import android.widget.Toast import android.widget.Toast import at.bitfire.davdroid.AccountSettings import at.bitfire.dav4android.Constants import at.bitfire.davdroid.model.Credentials import kotlinx.android.synthetic.main.fragment_eelo_authenticator.* import kotlinx.android.synthetic.main.fragment_eelo_authenticator.* import kotlinx.android.synthetic.main.fragment_eelo_authenticator.view.* import java.net.IDN import java.net.URI import java.net.URISyntaxException import java.util.logging.Level class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponseCallback { class EeloAuthenticatorFragment : Fragment() { private val extraAuthServiceDiscovery = "authServiceDiscovery" private val extraClientSecret = "clientSecret" private var authState: AuthState? = null private var authorizationService: AuthorizationService? = null private val bufferSize = 1024 private var userInfoJson: JSONObject? = null private fun isNetworkAvailable(): Boolean { private fun isNetworkAvailable(): Boolean { val connectivityManager = activity!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = activity!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager Loading @@ -57,305 +32,49 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_eelo_authenticator, container, false) val view = inflater.inflate(R.layout.fragment_eelo_authenticator, container, false) if (!isNetworkAvailable()) { view.login.setOnClickListener { login() } Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } // Initialise the authorization service authorizationService = AuthorizationService(context!!) activity?.intent?.let { if (!it.getBooleanExtra(LoginActivity.ACCOUNT_PROVIDER_EELO_AUTH_COMPLETE, false)) { // Get all the account providers val providers = IdentityProvider.getEnabledProviders(context) // Iterate over the account providers for (idp in providers) { val retrieveCallback = AuthorizationServiceConfiguration.RetrieveConfigurationCallback { serviceConfiguration, ex -> if (ex == null && serviceConfiguration != null) { makeAuthRequest(serviceConfiguration, idp) } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } if (idp.name == getString(R.string.eelo_name)) { // Get configurations for the eelo account provider idp.retrieveConfig(context, retrieveCallback) } } } else { val response = AuthorizationResponse.fromIntent(activity!!.intent) val ex = AuthorizationException.fromIntent(activity!!.intent) authState = AuthState(response, ex) if (response != null) { exchangeAuthorizationCode(response) } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } } return view return view } } private fun makeAuthRequest( private fun login() { serviceConfig: AuthorizationServiceConfiguration, idp: IdentityProvider) { if (!isNetworkAvailable()) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() activity!!.finish() } } val authRequest = AuthorizationRequest.Builder( if ((urlpwd_user_name.text.toString() != "") && (urlpwd_password.text.toString() != "")) { serviceConfig, validateLoginData()?.let { info -> idp.clientId, ResponseTypeValues.CODE, idp.redirectUri) .setScope(idp.scope) .build() authorizationService?.performAuthorizationRequest( authRequest, createPostAuthorizationIntent( context!!, authRequest, serviceConfig.discoveryDoc, idp.clientSecret), authorizationService?.createCustomTabsIntentBuilder()!! .build()) requireActivity().setResult(Activity.RESULT_OK) requireActivity().finish() } private fun createPostAuthorizationIntent( context: Context, request: AuthorizationRequest, discoveryDoc: AuthorizationServiceDiscovery?, clientSecret: String?): PendingIntent { val intent = Intent(context, LoginActivity::class.java) if (discoveryDoc != null) { intent.putExtra(extraAuthServiceDiscovery, discoveryDoc.docJson.toString()) } if (clientSecret != null) { intent.putExtra(extraClientSecret, clientSecret) } intent.putExtra(LoginActivity.SETUP_ACCOUNT_PROVIDER_TYPE, LoginActivity.ACCOUNT_PROVIDER_EELO) intent.putExtra(LoginActivity.ACCOUNT_PROVIDER_EELO_AUTH_COMPLETE, true) return PendingIntent.getActivity(context, request.hashCode(), intent, 0) } private fun exchangeAuthorizationCode(authorizationResponse: AuthorizationResponse) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } val additionalParams = HashMap<String, String?>() if (getClientSecretFromIntent(activity!!.intent) != null) { additionalParams["client_secret"] = getClientSecretFromIntent(activity!!.intent) } performTokenRequest(authorizationResponse.createTokenExchangeRequest(additionalParams)) } private fun getClientSecretFromIntent(intent: Intent): String? { return if (!intent.hasExtra(extraClientSecret)) { null } else intent.getStringExtra(extraClientSecret) } private fun performTokenRequest(request: TokenRequest) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } authorizationService?.performTokenRequest( request, this) } override fun onTokenRequestCompleted(response: TokenResponse?, ex: AuthorizationException?) { authState?.update(response, ex) validateLoginData("user1", authState!!)?.let { info -> DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) } } } else { // TODO Get the userId for future requests Toast.makeText(context, "Please enter a valid username and password", Toast.LENGTH_LONG).show() //getAccountInfo() } private fun getAccountInfo() { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } val discoveryDoc = getDiscoveryDocFromIntent(activity!!.intent) if (!authState!!.isAuthorized || discoveryDoc == null || discoveryDoc.userinfoEndpoint == null) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } else { object : AsyncTask<Void, Void, Void>() { override fun doInBackground(vararg params: Void): Void? { if (fetchUserInfo()) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } return null } }.execute() } } private fun getDiscoveryDocFromIntent(intent: Intent): AuthorizationServiceDiscovery? { if (!intent.hasExtra(extraAuthServiceDiscovery)) { return null } val discoveryJson = intent.getStringExtra(extraAuthServiceDiscovery) try { return AuthorizationServiceDiscovery(JSONObject(discoveryJson)) } catch (ex: JSONException) { throw IllegalStateException("Malformed JSON in discovery doc") } catch (ex: AuthorizationServiceDiscovery.MissingArgumentException) { throw IllegalStateException("Malformed JSON in discovery doc") } } private fun fetchUserInfo(): Boolean { var error = false if (authState!!.authorizationServiceConfiguration == null) { return true } authState!!.performActionWithFreshTokens(authorizationService!!, AuthState.AuthStateAction { accessToken, _, ex -> if (ex != null) { error = true return@AuthStateAction } val discoveryDoc = getDiscoveryDocFromIntent(activity!!.intent) ?: throw IllegalStateException("no available discovery doc") val userInfoEndpoint: URL try { userInfoEndpoint = URL(discoveryDoc.userinfoEndpoint!!.toString()) } catch (urlEx: MalformedURLException) { error = true return@AuthStateAction } var userInfoResponse: InputStream? = null try { val conn = userInfoEndpoint.openConnection() as HttpURLConnection conn.setRequestProperty("Authorization", "Bearer " + accessToken!!) conn.instanceFollowRedirects = false userInfoResponse = conn.inputStream val response = readStream(userInfoResponse) updateUserInfo(JSONObject(response)) } catch (ioEx: IOException) { error = true } catch (jsonEx: JSONException) { error = true } finally { if (userInfoResponse != null) { try { userInfoResponse.close() } catch (ioEx: IOException) { error = true } } } } } }) return error private fun validateLoginData(): LoginInfo? { } var valid = true @Throws(IOException::class) private fun readStream(stream: InputStream?): String { val br = BufferedReader(InputStreamReader(stream!!)) val buffer = CharArray(bufferSize) val sb = StringBuilder() var readCount = br.read(buffer) while (readCount != -1) { sb.append(buffer, 0, readCount) readCount = br.read(buffer) } return sb.toString() } private fun updateUserInfo(jsonObject: JSONObject) { Handler(Looper.getMainLooper()).post { userInfoJson = jsonObject onAccountInfoGotten() } } private fun onAccountInfoGotten() { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } if (userInfoJson != null) { val baseUrl = Uri.parse("https://drive.eelo.io") try { val uri = validateBaseUrl(baseUrl, false) { message -> var emailAddress = "" Toast.makeText(context, "Something went wrong. Please try again later", Toast.LENGTH_LONG).show() if (userInfoJson!!.has("email")) { valid = false emailAddress = userInfoJson!!.getString("email") } } validateLoginData(emailAddress, authState!!)?.let { info -> val userName = view!!.urlpwd_user_name.text.toString() DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) if (userName.isBlank()) { } Toast.makeText(context, "Invalid email address", Toast.LENGTH_LONG).show() } valid = false catch (ex: JSONException) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } val password = view!!.urlpwd_password.text.toString() if (password.isEmpty()) { Toast.makeText(context, "Invalid password", Toast.LENGTH_LONG).show() valid = false } } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } private fun validateLoginData(emailAddress: String, authState: AuthState): LoginInfo? { val baseUrl = Uri.parse("https://nc.test.eelo.io/remote.php/dav/") val uri = validateBaseUrl(baseUrl, false, { message -> view!!.urlpwd_base_url.error = message }) return if (uri != null) return if (valid && uri != null) LoginInfo(uri, emailAddress, null, authState, null) LoginInfo(uri, userName, password) else else null null } } Loading @@ -370,8 +89,7 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse else else try { try { host = IDN.toASCII(host) host = IDN.toASCII(host) } } catch (e: IllegalArgumentException) { catch (e: IllegalArgumentException) { Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e) Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e) } } Loading @@ -379,23 +97,14 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse val port = baseUrl.port val port = baseUrl.port try { try { uri = URI(baseUrl.scheme, null, host, port, path, null, null) uri = URI(baseUrl.scheme, null, host, port, path, null, null) } } catch (e: URISyntaxException) { catch (e: URISyntaxException) { reportError(e.localizedMessage) reportError(e.localizedMessage) } } } } else else reportError(getString(if (httpsRequired) reportError(getString(if (httpsRequired) R.string.login_url_must_be_https R.string.login_url_must_be_https else else R.string.login_url_must_be_http_or_https)) R.string.login_url_must_be_http_or_https)) return uri return uri } } override fun onDestroy() { super.onDestroy() authorizationService?.dispose() } } } app/src/main/res/layout/fragment_eelo_authenticator.xml +94 −8 Original line number Original line Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <!-- ~ Copyright © Ricki Hirner (bitfire web engineering). ~ All rights reserved. This program and the accompanying materials ~ are made available under the terms of the GNU Public License v3.0 ~ which accompanies this distribution, and is available at ~ http://www.gnu.org/licenses/gpl.html --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_height="match_parent"> tools:context=".ui.setup.GoogleAuthenticatorFragment"> <!-- We don't want the keyboard up when the user arrives in this initial screen --> <View android:layout_height="0dp" android:layout_width="0dp" android:focusable="true" android:focusableInTouchMode="true" android:contentDescription="@null" android:importantForAccessibility="no" tools:ignore="UnusedAttribute"> <requestFocus/> </View> <ProgressBar <ScrollView android:layout_width="match_parent" android:id="@+id/progress_bar" android:layout_height="0dp" android:layout_width="wrap_content" android:layout_weight="1"> <LinearLayout android:id="@+id/login_type_urlpwd_details" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="16dp" android:paddingTop="8dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="32dp" android:layout_marginTop="32dp" android:gravity="center" android:text="@string/login_eelo_title" android:textColor="#000000" android:textSize="22sp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingEnd="16dp" android:paddingStart="16dp"> <android.support.design.widget.TextInputEditText android:id="@+id/urlpwd_user_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> android:hint="@string/login_user_name" android:inputType="textEmailAddress" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingEnd="16dp" android:paddingStart="16dp" app:passwordToggleEnabled="true"> <android.support.design.widget.TextInputEditText android:id="@+id/urlpwd_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="monospace" android:hint="@string/login_password" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout> </LinearLayout> </ScrollView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/stepper_nav_bar"> <Space android:layout_width="0dp" android:layout_weight="1" style="@style/stepper_nav_button"/> <Button android:id="@+id/login" android:layout_width="0dp" android:layout_weight="1" android:text="@string/login_login" style="@style/stepper_nav_button"/> </RelativeLayout> </LinearLayout> No newline at end of file </LinearLayout> app/src/main/res/values/strings.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,7 @@ <!-- AddAccountActivity --> <!-- AddAccountActivity --> <string name="login_help_url">https://www.davdroid.com/tested-with/?pk_campaign=davdroid-app</string> <string name="login_help_url">https://www.davdroid.com/tested-with/?pk_campaign=davdroid-app</string> <string name="login_eelo_title">Login with an /e/ account</string> <string name="login_title">Add account</string> <string name="login_title">Add account</string> <string name="login_type_email">Login with email address</string> <string name="login_type_email">Login with email address</string> <string name="login_email_address">Email address</string> <string name="login_email_address">Email address</string> Loading Loading
app/src/main/java/at/bitfire/davdroid/ui/setup/EeloAuthenticatorFragment.kt +35 −326 Original line number Original line Diff line number Diff line package at.bitfire.davdroid.ui.setup package at.bitfire.davdroid.ui.setup import android.accounts.Account import android.accounts.AccountManager import android.app.Activity import android.app.PendingIntent import android.content.Context import android.content.Context import android.content.Intent import android.net.Uri import android.os.* import android.os.* import android.support.v4.app.Fragment import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.LayoutInflater import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewGroup import at.bitfire.dav4android.Constants import at.bitfire.davdroid.authorization.IdentityProvider import at.bitfire.davdroid.R import at.bitfire.davdroid.R import kotlinx.android.synthetic.main.login_credentials_fragment.view.* import net.openid.appauth.* import org.json.JSONException import org.json.JSONObject import java.io.BufferedReader import java.io.IOException import java.io.InputStream import java.io.InputStreamReader import java.net.* import java.util.HashMap import java.util.logging.Level import android.net.ConnectivityManager import android.net.ConnectivityManager import android.net.Uri import android.widget.Toast import android.widget.Toast import at.bitfire.davdroid.AccountSettings import at.bitfire.dav4android.Constants import at.bitfire.davdroid.model.Credentials import kotlinx.android.synthetic.main.fragment_eelo_authenticator.* import kotlinx.android.synthetic.main.fragment_eelo_authenticator.* import kotlinx.android.synthetic.main.fragment_eelo_authenticator.view.* import java.net.IDN import java.net.URI import java.net.URISyntaxException import java.util.logging.Level class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponseCallback { class EeloAuthenticatorFragment : Fragment() { private val extraAuthServiceDiscovery = "authServiceDiscovery" private val extraClientSecret = "clientSecret" private var authState: AuthState? = null private var authorizationService: AuthorizationService? = null private val bufferSize = 1024 private var userInfoJson: JSONObject? = null private fun isNetworkAvailable(): Boolean { private fun isNetworkAvailable(): Boolean { val connectivityManager = activity!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = activity!!.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager Loading @@ -57,305 +32,49 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_eelo_authenticator, container, false) val view = inflater.inflate(R.layout.fragment_eelo_authenticator, container, false) if (!isNetworkAvailable()) { view.login.setOnClickListener { login() } Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } // Initialise the authorization service authorizationService = AuthorizationService(context!!) activity?.intent?.let { if (!it.getBooleanExtra(LoginActivity.ACCOUNT_PROVIDER_EELO_AUTH_COMPLETE, false)) { // Get all the account providers val providers = IdentityProvider.getEnabledProviders(context) // Iterate over the account providers for (idp in providers) { val retrieveCallback = AuthorizationServiceConfiguration.RetrieveConfigurationCallback { serviceConfiguration, ex -> if (ex == null && serviceConfiguration != null) { makeAuthRequest(serviceConfiguration, idp) } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } if (idp.name == getString(R.string.eelo_name)) { // Get configurations for the eelo account provider idp.retrieveConfig(context, retrieveCallback) } } } else { val response = AuthorizationResponse.fromIntent(activity!!.intent) val ex = AuthorizationException.fromIntent(activity!!.intent) authState = AuthState(response, ex) if (response != null) { exchangeAuthorizationCode(response) } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } } return view return view } } private fun makeAuthRequest( private fun login() { serviceConfig: AuthorizationServiceConfiguration, idp: IdentityProvider) { if (!isNetworkAvailable()) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() activity!!.finish() } } val authRequest = AuthorizationRequest.Builder( if ((urlpwd_user_name.text.toString() != "") && (urlpwd_password.text.toString() != "")) { serviceConfig, validateLoginData()?.let { info -> idp.clientId, ResponseTypeValues.CODE, idp.redirectUri) .setScope(idp.scope) .build() authorizationService?.performAuthorizationRequest( authRequest, createPostAuthorizationIntent( context!!, authRequest, serviceConfig.discoveryDoc, idp.clientSecret), authorizationService?.createCustomTabsIntentBuilder()!! .build()) requireActivity().setResult(Activity.RESULT_OK) requireActivity().finish() } private fun createPostAuthorizationIntent( context: Context, request: AuthorizationRequest, discoveryDoc: AuthorizationServiceDiscovery?, clientSecret: String?): PendingIntent { val intent = Intent(context, LoginActivity::class.java) if (discoveryDoc != null) { intent.putExtra(extraAuthServiceDiscovery, discoveryDoc.docJson.toString()) } if (clientSecret != null) { intent.putExtra(extraClientSecret, clientSecret) } intent.putExtra(LoginActivity.SETUP_ACCOUNT_PROVIDER_TYPE, LoginActivity.ACCOUNT_PROVIDER_EELO) intent.putExtra(LoginActivity.ACCOUNT_PROVIDER_EELO_AUTH_COMPLETE, true) return PendingIntent.getActivity(context, request.hashCode(), intent, 0) } private fun exchangeAuthorizationCode(authorizationResponse: AuthorizationResponse) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } val additionalParams = HashMap<String, String?>() if (getClientSecretFromIntent(activity!!.intent) != null) { additionalParams["client_secret"] = getClientSecretFromIntent(activity!!.intent) } performTokenRequest(authorizationResponse.createTokenExchangeRequest(additionalParams)) } private fun getClientSecretFromIntent(intent: Intent): String? { return if (!intent.hasExtra(extraClientSecret)) { null } else intent.getStringExtra(extraClientSecret) } private fun performTokenRequest(request: TokenRequest) { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } authorizationService?.performTokenRequest( request, this) } override fun onTokenRequestCompleted(response: TokenResponse?, ex: AuthorizationException?) { authState?.update(response, ex) validateLoginData("user1", authState!!)?.let { info -> DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) } } } else { // TODO Get the userId for future requests Toast.makeText(context, "Please enter a valid username and password", Toast.LENGTH_LONG).show() //getAccountInfo() } private fun getAccountInfo() { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } val discoveryDoc = getDiscoveryDocFromIntent(activity!!.intent) if (!authState!!.isAuthorized || discoveryDoc == null || discoveryDoc.userinfoEndpoint == null) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } else { object : AsyncTask<Void, Void, Void>() { override fun doInBackground(vararg params: Void): Void? { if (fetchUserInfo()) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } return null } }.execute() } } private fun getDiscoveryDocFromIntent(intent: Intent): AuthorizationServiceDiscovery? { if (!intent.hasExtra(extraAuthServiceDiscovery)) { return null } val discoveryJson = intent.getStringExtra(extraAuthServiceDiscovery) try { return AuthorizationServiceDiscovery(JSONObject(discoveryJson)) } catch (ex: JSONException) { throw IllegalStateException("Malformed JSON in discovery doc") } catch (ex: AuthorizationServiceDiscovery.MissingArgumentException) { throw IllegalStateException("Malformed JSON in discovery doc") } } private fun fetchUserInfo(): Boolean { var error = false if (authState!!.authorizationServiceConfiguration == null) { return true } authState!!.performActionWithFreshTokens(authorizationService!!, AuthState.AuthStateAction { accessToken, _, ex -> if (ex != null) { error = true return@AuthStateAction } val discoveryDoc = getDiscoveryDocFromIntent(activity!!.intent) ?: throw IllegalStateException("no available discovery doc") val userInfoEndpoint: URL try { userInfoEndpoint = URL(discoveryDoc.userinfoEndpoint!!.toString()) } catch (urlEx: MalformedURLException) { error = true return@AuthStateAction } var userInfoResponse: InputStream? = null try { val conn = userInfoEndpoint.openConnection() as HttpURLConnection conn.setRequestProperty("Authorization", "Bearer " + accessToken!!) conn.instanceFollowRedirects = false userInfoResponse = conn.inputStream val response = readStream(userInfoResponse) updateUserInfo(JSONObject(response)) } catch (ioEx: IOException) { error = true } catch (jsonEx: JSONException) { error = true } finally { if (userInfoResponse != null) { try { userInfoResponse.close() } catch (ioEx: IOException) { error = true } } } } } }) return error private fun validateLoginData(): LoginInfo? { } var valid = true @Throws(IOException::class) private fun readStream(stream: InputStream?): String { val br = BufferedReader(InputStreamReader(stream!!)) val buffer = CharArray(bufferSize) val sb = StringBuilder() var readCount = br.read(buffer) while (readCount != -1) { sb.append(buffer, 0, readCount) readCount = br.read(buffer) } return sb.toString() } private fun updateUserInfo(jsonObject: JSONObject) { Handler(Looper.getMainLooper()).post { userInfoJson = jsonObject onAccountInfoGotten() } } private fun onAccountInfoGotten() { if (!isNetworkAvailable()) { Toast.makeText(context, "Please check your internet connection", Toast.LENGTH_LONG).show() activity!!.finish() } if (userInfoJson != null) { val baseUrl = Uri.parse("https://drive.eelo.io") try { val uri = validateBaseUrl(baseUrl, false) { message -> var emailAddress = "" Toast.makeText(context, "Something went wrong. Please try again later", Toast.LENGTH_LONG).show() if (userInfoJson!!.has("email")) { valid = false emailAddress = userInfoJson!!.getString("email") } } validateLoginData(emailAddress, authState!!)?.let { info -> val userName = view!!.urlpwd_user_name.text.toString() DetectConfigurationFragment.newInstance(info).show(fragmentManager, null) if (userName.isBlank()) { } Toast.makeText(context, "Invalid email address", Toast.LENGTH_LONG).show() } valid = false catch (ex: JSONException) { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } val password = view!!.urlpwd_password.text.toString() if (password.isEmpty()) { Toast.makeText(context, "Invalid password", Toast.LENGTH_LONG).show() valid = false } } else { Toast.makeText(context, "Login failed, please try again later", Toast.LENGTH_LONG).show() activity!!.finish() } } private fun validateLoginData(emailAddress: String, authState: AuthState): LoginInfo? { val baseUrl = Uri.parse("https://nc.test.eelo.io/remote.php/dav/") val uri = validateBaseUrl(baseUrl, false, { message -> view!!.urlpwd_base_url.error = message }) return if (uri != null) return if (valid && uri != null) LoginInfo(uri, emailAddress, null, authState, null) LoginInfo(uri, userName, password) else else null null } } Loading @@ -370,8 +89,7 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse else else try { try { host = IDN.toASCII(host) host = IDN.toASCII(host) } } catch (e: IllegalArgumentException) { catch (e: IllegalArgumentException) { Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e) Constants.log.log(Level.WARNING, "Host name not conforming to RFC 3490", e) } } Loading @@ -379,23 +97,14 @@ class EeloAuthenticatorFragment : Fragment(), AuthorizationService.TokenResponse val port = baseUrl.port val port = baseUrl.port try { try { uri = URI(baseUrl.scheme, null, host, port, path, null, null) uri = URI(baseUrl.scheme, null, host, port, path, null, null) } } catch (e: URISyntaxException) { catch (e: URISyntaxException) { reportError(e.localizedMessage) reportError(e.localizedMessage) } } } } else else reportError(getString(if (httpsRequired) reportError(getString(if (httpsRequired) R.string.login_url_must_be_https R.string.login_url_must_be_https else else R.string.login_url_must_be_http_or_https)) R.string.login_url_must_be_http_or_https)) return uri return uri } } override fun onDestroy() { super.onDestroy() authorizationService?.dispose() } } }
app/src/main/res/layout/fragment_eelo_authenticator.xml +94 −8 Original line number Original line Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <!-- ~ Copyright © Ricki Hirner (bitfire web engineering). ~ All rights reserved. This program and the accompanying materials ~ are made available under the terms of the GNU Public License v3.0 ~ which accompanies this distribution, and is available at ~ http://www.gnu.org/licenses/gpl.html --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_height="match_parent"> tools:context=".ui.setup.GoogleAuthenticatorFragment"> <!-- We don't want the keyboard up when the user arrives in this initial screen --> <View android:layout_height="0dp" android:layout_width="0dp" android:focusable="true" android:focusableInTouchMode="true" android:contentDescription="@null" android:importantForAccessibility="no" tools:ignore="UnusedAttribute"> <requestFocus/> </View> <ProgressBar <ScrollView android:layout_width="match_parent" android:id="@+id/progress_bar" android:layout_height="0dp" android:layout_width="wrap_content" android:layout_weight="1"> <LinearLayout android:id="@+id/login_type_urlpwd_details" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="16dp" android:paddingTop="8dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="32dp" android:layout_marginTop="32dp" android:gravity="center" android:text="@string/login_eelo_title" android:textColor="#000000" android:textSize="22sp" /> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingEnd="16dp" android:paddingStart="16dp"> <android.support.design.widget.TextInputEditText android:id="@+id/urlpwd_user_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> android:hint="@string/login_user_name" android:inputType="textEmailAddress" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingEnd="16dp" android:paddingStart="16dp" app:passwordToggleEnabled="true"> <android.support.design.widget.TextInputEditText android:id="@+id/urlpwd_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="monospace" android:hint="@string/login_password" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout> </LinearLayout> </ScrollView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/stepper_nav_bar"> <Space android:layout_width="0dp" android:layout_weight="1" style="@style/stepper_nav_button"/> <Button android:id="@+id/login" android:layout_width="0dp" android:layout_weight="1" android:text="@string/login_login" style="@style/stepper_nav_button"/> </RelativeLayout> </LinearLayout> No newline at end of file </LinearLayout>
app/src/main/res/values/strings.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,7 @@ <!-- AddAccountActivity --> <!-- AddAccountActivity --> <string name="login_help_url">https://www.davdroid.com/tested-with/?pk_campaign=davdroid-app</string> <string name="login_help_url">https://www.davdroid.com/tested-with/?pk_campaign=davdroid-app</string> <string name="login_eelo_title">Login with an /e/ account</string> <string name="login_title">Add account</string> <string name="login_title">Add account</string> <string name="login_type_email">Login with email address</string> <string name="login_type_email">Login with email address</string> <string name="login_email_address">Email address</string> <string name="login_email_address">Email address</string> Loading