Commit 43391e73 authored by Lucio Maciel's avatar Lucio Maciel

Add Two Factor auth.

parent b376f450
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.presentation.LoginView
import chat.rocket.android.authentication.presentation.TwoFAView
import chat.rocket.android.authentication.ui.LoginFragment
import chat.rocket.android.authentication.ui.TwoFAFragment
import dagger.Module
import dagger.Provides
@Module
class TwoFAFragmentModule {
@Provides
fun loginView(frag: TwoFAFragment): TwoFAView {
return frag
}
}
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.ui.TwoFAFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(TwoFAFragmentModule::class))
abstract fun provideTwoFAFragment(): TwoFAFragment
}
......@@ -6,6 +6,7 @@ import chat.rocket.android.app.MainActivity
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.authentication.ui.LoginFragment
import chat.rocket.android.authentication.ui.SignupFragment
import chat.rocket.android.authentication.ui.TwoFAFragment
import chat.rocket.android.util.addFragmentBackStack
class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
......@@ -26,7 +27,15 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
activity.finish()
}
fun toTwoFA(server: String, username: String, password: String) {
currentServer = server
activity.addFragmentBackStack("twoFAFragment", R.id.fragment_container) {
TwoFAFragment.newInstance(server, username, password)
}
}
fun toSignUp(server: String) {
currentServer = server
activity.addFragmentBackStack("signupFragment", R.id.fragment_container) {
SignupFragment.newInstance(server)
}
......
package chat.rocket.android.authentication.presentation
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
......@@ -39,6 +40,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
navigator.toChatList()
} catch (ex: RocketChatException) {
view.hideProgress()
when(ex) {
is RocketChatAuthException ->
if (ex.error?.contentEquals("totp-required") == true) {
navigator.toTwoFA(navigator.currentServer!!, username, password)
}
}
view.onLoginError(ex.message)
}
}
......
package chat.rocket.android.authentication.presentation
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import javax.inject.Inject
class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val navigator: AuthenticationNavigator,
private val okHttpClient: OkHttpClient,
private val logger: PlatformLogger,
private val repository: AuthTokenRepository) {
var job: Job? = null
val client: RocketChatClient = RocketChatClient.create {
httpClient = okHttpClient
restUrl = HttpUrl.parse(navigator.currentServer)!!
websocketUrl = navigator.currentServer!!
tokenRepository = repository
platformLogger = logger
}
fun authenticate(username: String, password: String, pin: String) {
// TODO - validate input
job = launch(UI) {
view.showProgress()
try {
val token = client.login(username, password, pin)
view.hideProgress()
navigator.toChatList()
} catch (ex: RocketChatException) {
view.hideProgress()
view.onLoginError(ex.message)
}
}
}
fun unbind() {
job?.let {
it.cancel()
}.also { null }
}
fun signup() {
navigator.toSignUp(navigator.currentServer!!)
}
}
\ No newline at end of file
package chat.rocket.android.authentication.presentation
interface TwoFAView : LoginView
\ No newline at end of file
package chat.rocket.android.authentication.ui
import DrawableHelper
import android.app.ProgressDialog
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
......@@ -8,10 +9,54 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.authentication.presentation.TwoFAPresenter
import chat.rocket.android.authentication.presentation.TwoFAView
import chat.rocket.android.util.content
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject
class TwoFAFragment : Fragment(), TwoFAView {
companion object {
private const val SERVER_URL = "server_url"
private const val USERNAME = "username"
private const val PASSWORD = "password"
fun newInstance(url: String, username: String, password: String) = TwoFAFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_URL, url)
putString(USERNAME, username)
putString(PASSWORD, password)
}
}
}
var progress: ProgressDialog? = null
lateinit var serverUrl: String
lateinit var username: String
lateinit var password: String
@Inject
lateinit var presenter: TwoFAPresenter
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
// TODO - research a better way to initialize parameters on fragments.
serverUrl = arguments?.getString(SERVER_URL) ?: "https://open.rocket.chat"
username = arguments?.getString(USERNAME) ?: ""
password = arguments?.getString(PASSWORD) ?: ""
}
override fun onDestroy() {
presenter.unbind()
super.onDestroy()
}
class TwoFAFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_authentication_two_fa, container, false)
......@@ -23,6 +68,10 @@ class TwoFAFragment : Fragment() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
button_log_in.setOnClickListener {
presenter.authenticate(username, password, text_two_factor_auth.content)
}
}
private fun tintEditTextDrawableStart() {
......@@ -34,4 +83,21 @@ class TwoFAFragment : Fragment() {
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
}
override fun showProgress() {
// TODO - change for a proper progress indicator
progress = ProgressDialog.show(activity, "Authenticating", "Verifying user credentials")
}
override fun hideProgress() {
progress?.apply {
cancel()
}
progress = null
}
override fun onLoginError(message: String?) {
// TODO - show a proper error message
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
}
\ No newline at end of file
package chat.rocket.android.dagger.module
import chat.rocket.android.app.MainActivity
import chat.rocket.android.authentication.di.AuthenticationModule
import chat.rocket.android.authentication.di.LoginFragmentProvider
import chat.rocket.android.authentication.di.ServerFragmentProvider
import chat.rocket.android.authentication.di.SignupFragmentProvider
import chat.rocket.android.authentication.di.*
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
......@@ -18,7 +15,8 @@ abstract class ActivityBuilder {
AuthenticationModule::class,
LoginFragmentProvider::class,
ServerFragmentProvider::class,
SignupFragmentProvider::class
SignupFragmentProvider::class,
TwoFAFragmentProvider::class
))
abstract fun bindAuthenticationActivity(): AuthenticationActivity
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment