Implement updated authentication
This commit is contained in:
parent
6f24feed7b
commit
74601bc18d
21 changed files with 64 additions and 73 deletions
|
@ -20,8 +20,9 @@ android {
|
|||
useSupportLibrary = true
|
||||
}
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
buildConfigField "String", "API_URL", "\"http://192.168.86.92:8080/\""
|
||||
// buildConfigField "String", "API_URL", "\"http://192.168.86.92:8080/\""
|
||||
// buildConfigField "String", "API_URL", "\"http://10.0.2.2:8080/\""
|
||||
buildConfigField "String", "API_URL", "\"https://budget-api.intra.wbrawner.com/\""
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
@ -38,7 +39,7 @@ android {
|
|||
dependencies {
|
||||
implementation project(':common')
|
||||
implementation project(':budgetlib')
|
||||
implementation project(':auth')
|
||||
implementation project(':storage')
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package com.wbrawner.budget.di
|
||||
|
||||
import android.content.Context
|
||||
import com.wbrawner.budget.auth.AuthModule
|
||||
import com.wbrawner.budget.lib.network.NetworkModule
|
||||
import com.wbrawner.budget.storage.StorageModule
|
||||
import com.wbrawner.budget.ui.MainActivity
|
||||
import com.wbrawner.budget.ui.SplashActivity
|
||||
import com.wbrawner.budget.ui.SplashViewModelMapper
|
||||
|
@ -29,7 +29,7 @@ import javax.inject.Named
|
|||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Component(modules = [AuthModule::class, AppModule::class, NetworkModule::class])
|
||||
@Component(modules = [StorageModule::class, AppModule::class, NetworkModule::class])
|
||||
interface AppComponent {
|
||||
fun inject(fragment: OverviewFragment)
|
||||
fun inject(fragment: LoginFragment)
|
||||
|
|
|
@ -2,9 +2,8 @@ package com.wbrawner.budget.ui
|
|||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wbrawner.budget.auth.CredentialsProvider
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.di.ViewModelKey
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
@ -12,14 +11,13 @@ import dagger.multibindings.IntoMap
|
|||
import javax.inject.Inject
|
||||
|
||||
class SplashViewModel @Inject constructor(
|
||||
private val credentialsProvider: CredentialsProvider,
|
||||
private val budgetRepository: BudgetRepository
|
||||
private val userRepository: UserRepository
|
||||
) : ViewModel() {
|
||||
val isLoading: MutableLiveData<Boolean> = MutableLiveData(false)
|
||||
|
||||
suspend fun checkForExistingCredentials(): User? {
|
||||
return try {
|
||||
login(credentialsProvider.username, credentialsProvider.password)
|
||||
userRepository.getProfile()
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
@ -27,9 +25,8 @@ class SplashViewModel @Inject constructor(
|
|||
|
||||
suspend fun login(username: String, password: String): User {
|
||||
isLoading.value = true
|
||||
credentialsProvider.saveCredentials(username, password)
|
||||
return try {
|
||||
budgetRepository.login(username, password)
|
||||
userRepository.login(username, password)
|
||||
} catch (e: java.lang.Exception) {
|
||||
isLoading.value = false
|
||||
throw e
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package com.wbrawner.budget.auth
|
||||
|
||||
import android.content.SharedPreferences
|
||||
|
||||
interface CredentialsProvider {
|
||||
val username: String
|
||||
val password: String
|
||||
fun saveCredentials(username: String, password: String)
|
||||
}
|
||||
|
||||
private const val PREF_KEY_USERNAME = "PREF_KEY_USERNAME"
|
||||
private const val PREF_KEY_PASSWORD = "PREF_KEY_PASSWORD"
|
||||
|
||||
class SharedPreferencesCredentialsProvider(private val sharedPreferences: SharedPreferences) : CredentialsProvider {
|
||||
override val username: String = sharedPreferences.getString(PREF_KEY_USERNAME, null) ?: ""
|
||||
|
||||
override val password: String = sharedPreferences.getString(PREF_KEY_PASSWORD, null) ?: ""
|
||||
|
||||
override fun saveCredentials(username: String, password: String) {
|
||||
sharedPreferences.edit()
|
||||
.putString(PREF_KEY_USERNAME, username)
|
||||
.putString(PREF_KEY_PASSWORD, password)
|
||||
.apply()
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
implementation project(':auth')
|
||||
implementation project(':storage')
|
||||
// Retrofit
|
||||
api "com.squareup.retrofit2:retrofit:$rootProject.ext.retrofit"
|
||||
implementation "com.squareup.retrofit2:converter-moshi:$rootProject.ext.retrofit"
|
||||
|
|
|
@ -95,6 +95,9 @@ interface BudgetApiService {
|
|||
@POST("users/login")
|
||||
suspend fun login(@Body request: LoginRequest): User
|
||||
|
||||
@GET("users/me")
|
||||
suspend fun getProfile(): User
|
||||
|
||||
@GET("users/search")
|
||||
suspend fun searchUsers(@Query("query") query: String): List<User>
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package com.wbrawner.budget.lib.network
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Base64
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
||||
import com.wbrawner.budget.auth.CredentialsProvider
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.category.CategoryRepository
|
||||
import com.wbrawner.budget.common.transaction.TransactionRepository
|
||||
|
@ -13,10 +14,13 @@ import com.wbrawner.budget.lib.repository.NetworkTransactionRepository
|
|||
import com.wbrawner.budget.lib.repository.NetworkUserRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import javax.inject.Named
|
||||
|
||||
|
@ -28,25 +32,39 @@ class NetworkModule {
|
|||
.build()
|
||||
|
||||
@Provides
|
||||
fun provideOkHttpClient(credentialsProvider: CredentialsProvider): OkHttpClient = OkHttpClient.Builder()
|
||||
.addInterceptor {
|
||||
if (it.request().headers().get("Authorization") != null)
|
||||
return@addInterceptor it.proceed(it.request())
|
||||
val credentials = Credentials.basic(
|
||||
credentialsProvider.username,
|
||||
credentialsProvider.password
|
||||
)
|
||||
val newHeaders = it.request()
|
||||
.headers()
|
||||
.newBuilder()
|
||||
.add("Authorization: $credentials")
|
||||
.build()
|
||||
val newRequest = it.request()
|
||||
.newBuilder()
|
||||
.headers(newHeaders)
|
||||
.build()
|
||||
it.proceed(newRequest)
|
||||
fun provideCookieJar(sharedPreferences: SharedPreferences): CookieJar {
|
||||
return object : CookieJar {
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
|
||||
sharedPreferences.edit()
|
||||
.putString(
|
||||
url.host(),
|
||||
cookies.joinToString(separator = ",") {
|
||||
Base64.encode(it.toString().toByteArray(), 0)
|
||||
.toString(charset = Charset.forName("UTF-8"))
|
||||
}
|
||||
)
|
||||
.apply()
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
|
||||
return sharedPreferences.getString(url.host(), "")
|
||||
?.split(",")
|
||||
?.mapNotNull {
|
||||
Cookie.parse(
|
||||
url,
|
||||
Base64.decode(it, 0).toString(Charset.forName("UTF-8"))
|
||||
)
|
||||
}
|
||||
?.toMutableList()
|
||||
?: mutableListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideOkHttpClient(cookieJar: CookieJar): OkHttpClient = OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
// TODO: Add Gander interceptor
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -2,8 +2,6 @@ package com.wbrawner.budget.lib.repository
|
|||
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.LoginRequest
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -21,9 +19,6 @@ class NetworkBudgetRepository @Inject constructor(private val apiService: Budget
|
|||
|
||||
override suspend fun delete(id: Long) = apiService.deleteBudget(id)
|
||||
|
||||
override suspend fun login(username: String, password: String): User =
|
||||
apiService.login(LoginRequest(username, password))
|
||||
|
||||
override suspend fun getBalance(id: Long): Long = apiService.getBudgetBalance(id).balance
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
package com.wbrawner.budget.lib.repository
|
||||
|
||||
import com.wbrawner.budget.common.user.LoginRequest
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import javax.inject.Inject
|
||||
|
||||
class NetworkUserRepository @Inject constructor(private val apiService: BudgetApiService) : UserRepository {
|
||||
|
||||
override suspend fun login(username: String, password: String): User =
|
||||
apiService.login(LoginRequest(username, password))
|
||||
|
||||
override suspend fun getProfile(): User = apiService.getProfile()
|
||||
|
||||
override suspend fun create(newItem: User): User = apiService.newUser(newItem)
|
||||
|
||||
override suspend fun findAll(accountId: Long?): Collection<User> = apiService.getUsers(accountId)
|
||||
|
|
|
@ -10,7 +10,7 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
classpath 'io.fabric.tools:gradle:1.31.0'
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package com.wbrawner.budget.common.budget
|
||||
|
||||
import com.wbrawner.budget.common.Repository
|
||||
import com.wbrawner.budget.common.user.User
|
||||
|
||||
interface BudgetRepository : Repository<Budget, Long> {
|
||||
suspend fun login(username: String, password: String): User
|
||||
suspend fun getBalance(id: Long): Long
|
||||
}
|
|
@ -3,6 +3,8 @@ package com.wbrawner.budget.common.user
|
|||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface UserRepository : Repository<User, Long> {
|
||||
suspend fun login(username: String, password: String): User
|
||||
suspend fun getProfile(): User
|
||||
suspend fun findAll(accountId: Long? = null): Collection<User>
|
||||
suspend fun findAllByNameLike(query: String): Collection<User>
|
||||
}
|
|
@ -1 +1 @@
|
|||
include ':app', ':budgetlib', ':common', ':auth'
|
||||
include ':app', ':budgetlib', ':common', ':storage'
|
||||
|
|
0
auth/.gitignore → storage/.gitignore
vendored
0
auth/.gitignore → storage/.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
package com.wbrawner.budget.auth
|
||||
package com.wbrawner.budget.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
@ -10,12 +10,7 @@ import javax.inject.Singleton
|
|||
const val ENCRYPTED_SHARED_PREFS_FILE_NAME = "shared-prefs.encrypted"
|
||||
|
||||
@Module
|
||||
class AuthModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideCredentialsProvider(sharedPreferences: SharedPreferences): CredentialsProvider =
|
||||
SharedPreferencesCredentialsProvider(sharedPreferences)
|
||||
|
||||
class StorageModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSharedPreferences(context: Context): SharedPreferences =
|
Loading…
Reference in a new issue