Explorar o código

Merge pull request #4 from MrOzOn/secure_access_token

Secure access token
MrOzOn %!s(int64=5) %!d(string=hai) anos
pai
achega
81cab4bb64

+ 4 - 3
app/src/main/java/com/mrozon/healthdiary/di/FacadeComponent.kt

@@ -6,12 +6,12 @@ import com.mrozon.core_api.db.DatabaseProvider
 import com.mrozon.core_api.network.NetworkProvider
 import com.mrozon.core_api.providers.AppProvider
 import com.mrozon.core_api.providers.ProvidersFacade
-import com.mrozon.core_api.viewmodel.ViewModelsFactoryProvider
+import com.mrozon.core_api.security.SecurityTokenProvider
 import com.mrozon.healthdiary.App
 import dagger.Component
 
 @Component(
-    dependencies = [AppProvider::class, DatabaseProvider::class, NetworkProvider::class],
+    dependencies = [AppProvider::class, DatabaseProvider::class, NetworkProvider::class, SecurityTokenProvider::class],
     modules = [NavigationModule::class]
 )
 interface FacadeComponent : ProvidersFacade {
@@ -22,7 +22,8 @@ interface FacadeComponent : ProvidersFacade {
             DaggerFacadeComponent.builder()
                 .appProvider(AppComponent.create(application))
                 .databaseProvider(CoreProvidersFactory.createDatabaseBuilder(AppComponent.create(application)))
-                .networkProvider(CoreProvidersFactory.createNetworkBuilder())
+                .networkProvider(CoreProvidersFactory.createNetworkBuilder(AppComponent.create(application)))
+                .securityTokenProvider(CoreProvidersFactory.createSecurityTokenBuilder(AppComponent.create(application)))
                 .build()
     }
 

+ 7 - 2
core/src/main/java/com/mrozon/core/CoreProvidersFactory.kt

@@ -3,6 +3,8 @@ package com.mrozon.core
 import com.mrozon.core_api.db.DatabaseProvider
 import com.mrozon.core_api.network.NetworkProvider
 import com.mrozon.core_api.providers.AppProvider
+import com.mrozon.core_api.security.SecurityTokenProvider
+import com.mrozon.core_impl.crypto.DaggerSecurityTokenComponent
 import com.mrozon.core_impl.db.DaggerDatabaseComponent
 import com.mrozon.core_impl.network.DaggerNetworkComponent
 
@@ -11,8 +13,11 @@ object CoreProvidersFactory {
         return DaggerDatabaseComponent.builder().appProvider(appProvider).build()
     }
 
-    fun createNetworkBuilder(): NetworkProvider {
-        return DaggerNetworkComponent.create()
+    fun createNetworkBuilder(appProvider: AppProvider): NetworkProvider {
+        return DaggerNetworkComponent.builder().securityTokenProvider(createSecurityTokenBuilder(appProvider)).build()
     }
 
+    fun createSecurityTokenBuilder(appProvider: AppProvider): SecurityTokenProvider {
+        return DaggerSecurityTokenComponent.builder().appProvider(appProvider).build()
+    }
 }

+ 3 - 3
core_api/src/main/java/com/mrozon/core_api/db/HealthDiaryDao.kt

@@ -18,9 +18,9 @@ interface HealthDiaryDao {
     @Delete
     suspend fun deleteUser(userDb: UserDb)
 
-    // TOKEN
-    @Query("SELECT user_token from user_table LIMIT 1")
-    fun getAccessToken(): String
+//    // TOKEN
+//    @Query("SELECT user_token from user_table LIMIT 1")
+//    fun getAccessToken(): String
 
     // PERSON
     @Query("SELECT * FROM person_table")

+ 5 - 6
core_api/src/main/java/com/mrozon/core_api/network/HealthDiaryService.kt

@@ -17,21 +17,20 @@ interface HealthDiaryService {
     suspend fun registerUser(@Body body: RegisterRequest): Response<RegisterResponse>
 
     @GET("patients/")
-    suspend fun getPersons(@Header("Authorization") token: String): Response<List<PersonResponse>>
+    suspend fun getPersons(): Response<List<PersonResponse>>
 
     @POST("patients/")
-    suspend fun addPerson(@Header("Authorization") token: String, @Body body: PersonRequest): Response<PersonResponse>
+    suspend fun addPerson(@Body body: PersonRequest): Response<PersonResponse>
 
     @DELETE("patients/{id}/")
-    suspend fun deletePerson(@Header("Authorization") token: String, @Path("id") id: String): Response<Unit>
+    suspend fun deletePerson(@Path("id") id: String): Response<Unit>
 
     @PUT("patients/{id}/")
-    suspend fun editPerson(@Header("Authorization") token: String,
-                           @Path("id") id: String,
+    suspend fun editPerson(@Path("id") id: String,
                            @Body body: PersonRequest): Response<PersonResponse>
 
     @POST("add-user-to-patient/")
-    suspend fun sharePerson(@Header("Authorization") token: String, @Body body: SharePersonRequest): Response<PersonResponse>
+    suspend fun sharePerson(@Body body: SharePersonRequest): Response<PersonResponse>
 
 //    @GET("lego/themes/")
 //    suspend fun getThemes(@Query("page") page: Int? = null,

+ 1 - 1
core_api/src/main/java/com/mrozon/core_api/network/model/LoginResponse.kt

@@ -17,7 +17,7 @@ fun LoginResponse.toUserDb(): UserDb {
         email = email,
         firstname = first_name,
         lastname = last_name,
-        token = token
+        token = ""
     )
 }
 

+ 3 - 1
core_api/src/main/java/com/mrozon/core_api/providers/ProvidersFacade.kt

@@ -3,5 +3,7 @@ package com.mrozon.core_api.providers
 import com.mrozon.core_api.db.DatabaseProvider
 import com.mrozon.core_api.navigation.NavigatorProvider
 import com.mrozon.core_api.network.NetworkProvider
+import com.mrozon.core_api.security.SecurityTokenProvider
 
-interface ProvidersFacade : NetworkProvider, DatabaseProvider, AppProvider, NavigatorProvider
+interface ProvidersFacade : NetworkProvider, DatabaseProvider, AppProvider,
+    NavigatorProvider, SecurityTokenProvider

+ 5 - 0
core_api/src/main/java/com/mrozon/core_api/security/SecurityTokenProvider.kt

@@ -0,0 +1,5 @@
+package com.mrozon.core_api.security
+
+interface SecurityTokenProvider {
+    fun provideSecurityTokenService(): SecurityTokenService
+}

+ 6 - 0
core_api/src/main/java/com/mrozon/core_api/security/SecurityTokenService.kt

@@ -0,0 +1,6 @@
+package com.mrozon.core_api.security
+
+interface SecurityTokenService {
+    fun loadAccessToken(): String
+    fun saveAccessToken(string: String)
+}

+ 1 - 0
core_impl/build.gradle

@@ -41,6 +41,7 @@ dependencies {
     implementation gson
     implementation converterGson
     implementation navigationFragment
+    implementation securityCrypto
 
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

+ 13 - 0
core_impl/src/main/java/com/mrozon/core_impl/crypto/SecurityTokenComponent.kt

@@ -0,0 +1,13 @@
+package com.mrozon.core_impl.crypto
+
+import com.mrozon.core_api.providers.AppProvider
+import com.mrozon.core_api.security.SecurityTokenProvider
+import dagger.Component
+import javax.inject.Singleton
+
+@Singleton
+@Component(
+    dependencies = [AppProvider::class],
+    modules = [SecurityTokenModule::class]
+)
+interface SecurityTokenComponent: SecurityTokenProvider

+ 19 - 0
core_impl/src/main/java/com/mrozon/core_impl/crypto/SecurityTokenModule.kt

@@ -0,0 +1,19 @@
+package com.mrozon.core_impl.crypto
+
+import android.content.SharedPreferences
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKey
+import com.mrozon.core_api.navigation.SplashNavigator
+import com.mrozon.core_api.security.SecurityTokenService
+import dagger.Binds
+import dagger.Module
+import dagger.Reusable
+
+@Module
+interface SecurityTokenModule {
+
+    @Reusable
+    @Binds
+    fun  provideSecurityTokenService(service: SecurityTokenServiceImpl): SecurityTokenService
+
+}

+ 44 - 0
core_impl/src/main/java/com/mrozon/core_impl/crypto/SecurityTokenServiceImpl.kt

@@ -0,0 +1,44 @@
+package com.mrozon.core_impl.crypto
+
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKey
+import com.mrozon.core_api.security.SecurityTokenProvider
+import com.mrozon.core_api.security.SecurityTokenService
+import javax.inject.Inject
+
+class SecurityTokenServiceImpl @Inject constructor(
+    context: Context
+): SecurityTokenService {
+
+    companion object {
+        const val FILE_NAME = "access_token"
+        const val FIELD_NAME = "token"
+    }
+
+    private var ssp: SharedPreferences
+
+    init {
+        val masterKey = MasterKey.Builder(context)
+            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+            .build()
+        ssp = EncryptedSharedPreferences.create(
+            context,
+            FILE_NAME,
+            masterKey,
+            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+        )
+    }
+
+    override fun loadAccessToken(): String {
+        return ssp.getString(FIELD_NAME,"")?:""
+    }
+
+    override fun saveAccessToken(string: String) {
+        ssp.edit()
+            .putString(FIELD_NAME,string)
+            .apply()
+    }
+}

+ 4 - 0
core_impl/src/main/java/com/mrozon/core_impl/network/NetworkComponent.kt

@@ -1,11 +1,15 @@
 package com.mrozon.core_impl.network
 
 import com.mrozon.core_api.network.NetworkProvider
+import com.mrozon.core_api.providers.AppProvider
+import com.mrozon.core_api.security.SecurityTokenProvider
+import com.mrozon.core_api.security.SecurityTokenService
 import dagger.Component
 import javax.inject.Singleton
 
 @Singleton
 @Component(
+    dependencies = [SecurityTokenProvider::class],
     modules = [NetworkModule::class]
 )
 interface NetworkComponent : NetworkProvider

+ 10 - 1
core_impl/src/main/java/com/mrozon/core_impl/network/NetworkModule.kt

@@ -4,6 +4,7 @@ import okhttp3.logging.HttpLoggingInterceptor.Level.BODY
 import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
 import com.google.gson.Gson
 import com.mrozon.core_api.network.HealthDiaryService
+import com.mrozon.core_api.security.SecurityTokenService
 import com.mrozon.core_impl.BuildConfig
 import dagger.Module
 import dagger.Provides
@@ -30,9 +31,17 @@ class NetworkModule {
         HttpLoggingInterceptor().apply { level = if (BuildConfig.DEBUG) BODY else NONE }
 
     @Provides
-    fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient =
+    fun provideOkHttpClient(interceptor: HttpLoggingInterceptor, security: SecurityTokenService): OkHttpClient =
         OkHttpClient.Builder()
             .addInterceptor(interceptor)
+            .addInterceptor { chain ->
+                val newRequestBuilder = chain.request().newBuilder()
+                val token = security.loadAccessToken()
+                if (token.isNotEmpty()) {
+                    newRequestBuilder.addHeader("Authorization", "Token $token")
+                }
+                chain.proceed(newRequestBuilder.build())
+            }
             .build()
 
     @Singleton

+ 4 - 2
feature_auth/src/androidTest/java/com/mrozon/feature_auth/di/FacadeComponent.kt

@@ -6,12 +6,13 @@ import com.mrozon.core_api.db.DatabaseProvider
 import com.mrozon.core_api.network.NetworkProvider
 import com.mrozon.core_api.providers.AppProvider
 import com.mrozon.core_api.providers.ProvidersFacade
+import com.mrozon.core_api.security.SecurityTokenProvider
 import com.mrozon.core_api.viewmodel.ViewModelsFactoryProvider
 import com.mrozon.feature_auth.presentation.TestApp
 import dagger.Component
 
 @Component(
-    dependencies = [AppProvider::class, DatabaseProvider::class, NetworkProvider::class],
+    dependencies = [AppProvider::class, DatabaseProvider::class, NetworkProvider::class, SecurityTokenProvider::class],
     modules = [NavigationModule::class]
 )
 interface FacadeComponent : ProvidersFacade {
@@ -22,7 +23,8 @@ interface FacadeComponent : ProvidersFacade {
             DaggerFacadeComponent.builder()
                 .appProvider(AppComponent.create(application))
                 .databaseProvider(CoreProvidersFactory.createDatabaseBuilder(AppComponent.create(application)))
-                .networkProvider(CoreProvidersFactory.createNetworkBuilder())
+                .networkProvider(CoreProvidersFactory.createNetworkBuilder(AppComponent.create(application)))
+                .securityTokenProvider(CoreProvidersFactory.createSecurityTokenBuilder(AppComponent.create(application)))
                 .build()
     }
 

+ 4 - 2
feature_auth/src/main/java/com/mrozon/feature_auth/data/UserAuthRepositoryImpl.kt

@@ -4,6 +4,7 @@ import com.mrozon.core_api.db.HealthDiaryDao
 import com.mrozon.core_api.entity.User
 import com.mrozon.core_api.mapper.UserToUserDbMapper
 import com.mrozon.core_api.network.model.*
+import com.mrozon.core_api.security.SecurityTokenService
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flow
 import javax.inject.Inject
@@ -16,7 +17,8 @@ import timber.log.Timber
 
 @Singleton
 class UserAuthRepositoryImpl @Inject constructor(private val userAuthRemoteDataSource: UserAuthRemoteDataSource,
-                                                 private val dao: HealthDiaryDao
+                                                 private val dao: HealthDiaryDao,
+                                                 private val securityTokenService: SecurityTokenService
 
 ): UserAuthRepository {
 
@@ -40,7 +42,7 @@ class UserAuthRepositoryImpl @Inject constructor(private val userAuthRemoteDataS
             val request = LoginRequest(username = userName, password = password)
             val response = userAuthRemoteDataSource.loginUser(request)
             if (response.status == Result.Status.SUCCESS) {
-                Timber.d("sahdksdh")
+                securityTokenService.saveAccessToken(response.data!!.token)
                 dao.insertUser(response.data!!.toUserDb())
                 emit(success(response.data!!.toUser()))
             } else if (response.status == Result.Status.ERROR) {

+ 5 - 10
feature_person/src/main/java/com/mrozon/feature_person/data/PersonRemoteDataSource.kt

@@ -12,31 +12,26 @@ class PersonRemoteDataSource @Inject constructor(private val service: HealthDiar
 
     suspend fun getPersons()
             = getResult {
-                val token = "Token "+dao.getAccessToken()
-                service.getPersons(token)
+                service.getPersons()
             }
 
     suspend fun addPersons(personRequest: PersonRequest)
             = getResult {
-        val token = "Token "+dao.getAccessToken()
-        service.addPerson(token, personRequest)
+        service.addPerson(personRequest)
     }
 
     suspend fun deletePerson(id: Long)
             = getResult {
-        val token = "Token "+dao.getAccessToken()
-        service.deletePerson(token, id.toString())
+        service.deletePerson(id.toString())
     }
 
     suspend fun editPerson(id: Long, personRequest: PersonRequest)
             = getResult {
-        val token = "Token "+dao.getAccessToken()
-        service.editPerson(token, id.toString(), personRequest)
+        service.editPerson(id.toString(), personRequest)
     }
 
     suspend fun sharePerson(sharePersonRequest: SharePersonRequest)
             = getResult {
-        val token = "Token "+dao.getAccessToken()
-        service.sharePerson(token, sharePersonRequest)
+        service.sharePerson(sharePersonRequest)
     }
 }

+ 3 - 1
scripts/deps_versions.gradle

@@ -24,6 +24,7 @@ ext {
     robolectricVersion = "4.4"
     androidXCoreTestVersion = "2.1.0"
     androidXTestVersion = "1.1.0"
+    securityCryptoVersion = "1.1.0-alpha02"
 
     // DI
     dagger = "com.google.dagger:dagger:$daggerVersion"
@@ -71,5 +72,6 @@ ext {
     androidxCore = "androidx.core:core-ktx:$androidxCoreVersion"
     // Kotlin
     kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
-
+    //Crytpo
+    securityCrypto = "androidx.security:security-crypto:$securityCryptoVersion"
 }