Skip to content

Commit

Permalink
- add revocation reload functionality with last time of sync.; (eu-di…
Browse files Browse the repository at this point in the history
…gital-green-certificates#296)

- fix bug with eTag deletion on 304 code;
- fix timestamp format when delete expired data;
  • Loading branch information
MykhailoNester authored Mar 28, 2022
1 parent b384a51 commit e99e5d8
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ interface VerifierRepository {

suspend fun getCertificatesBy(kid: String): List<Certificate>

fun getLastSyncTimeMillis(): LiveData<Long>
fun getLastPubKeysSyncTimeMillis(): LiveData<Long>

fun getLastRevocationSyncTimeMillis(): Long
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package dgca.verifier.app.android.data

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import dcc.app.revocation.data.RevocationPreferences
import dgca.verifier.app.android.data.local.AppDatabase
import dgca.verifier.app.android.data.local.Preferences
import dgca.verifier.app.android.data.local.model.Key
Expand All @@ -42,13 +43,14 @@ import javax.inject.Inject
class VerifierRepositoryImpl @Inject constructor(
private val apiService: ApiService,
private val preferences: Preferences,
private val revocationPreferences: RevocationPreferences,
private val db: AppDatabase,
private val keyStoreCryptor: KeyStoreCryptor
) : BaseRepository(), VerifierRepository {

private val validCertList = mutableListOf<String>()
private val mutex = Mutex()
private val lastSyncLiveData: MutableLiveData<Long> = MutableLiveData(preferences.lastKeysSyncTimeMillis)
private val lastSyncLiveData = MutableLiveData(preferences.lastKeysSyncTimeMillis)

override suspend fun fetchCertificates(statusUrl: String, updateUrl: String): Boolean? {
mutex.withLock {
Expand All @@ -73,7 +75,9 @@ class VerifierRepositoryImpl @Inject constructor(
keyStoreCryptor.decrypt(it.key)?.base64ToX509Certificate()!!
}

override fun getLastSyncTimeMillis(): LiveData<Long> = lastSyncLiveData
override fun getLastPubKeysSyncTimeMillis(): LiveData<Long> = lastSyncLiveData

override fun getLastRevocationSyncTimeMillis(): Long = revocationPreferences.lastRevocationSyncTimeMillis

private suspend fun fetchCertificate(url: String, resumeToken: Long) {
val tokenFormatted = if (resumeToken == -1L) "" else resumeToken.toString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
binding.syncPublicKeys.setOnClickListener { viewModel.syncPublicKeys() }
binding.version.text = getString(R.string.version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)

viewModel.inProgress.observe(viewLifecycleOwner, {
viewModel.inProgress.observe(viewLifecycleOwner) {
binding.privacyInformation.isClickable = it != true
binding.licenses.isClickable = it != true
binding.syncPublicKeys.isClickable = it != true
binding.progressBar.visibility = if (it == true) View.VISIBLE else View.GONE
})
viewModel.lastSyncLiveData.observe(viewLifecycleOwner, {
}
viewModel.lastSyncLiveData.observe(viewLifecycleOwner) {
if (it <= 0) {
binding.lastUpdate.visibility = View.GONE
} else {
Expand All @@ -83,7 +83,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
it.toLocalDateTime().formatWith(LAST_UPDATE_DATE_TIME_FORMAT)
)
}
})
}
viewModel.debugModeState.observe(viewLifecycleOwner) {
setUpDebugModeButton(it)
}
Expand All @@ -92,6 +92,19 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
SettingsFragmentDirections.actionSettingsFragmentToVerificationResultFragment()
findNavController().navigate(action)
}

binding.syncRevocation.setOnClickListener { viewModel.syncRevocation() }
viewModel.lastRevocationSyncTime.observe(viewLifecycleOwner) {
if (it <= 0) {
binding.lastRevocationUpdate.visibility = View.GONE
} else {
binding.lastRevocationUpdate.visibility = View.VISIBLE
binding.lastRevocationUpdate.text = getString(
R.string.last_updated,
it.toLocalDateTime().formatWith(LAST_UPDATE_DATE_TIME_FORMAT)
)
}
}
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package dgca.verifier.app.android.settings

import androidx.lifecycle.*
import dagger.hilt.android.lifecycle.HiltViewModel
import dcc.app.revocation.domain.usacase.GetRevocationDataUseCase
import dgca.verifier.app.android.BuildConfig
import dgca.verifier.app.android.data.ConfigRepository
import dgca.verifier.app.android.data.VerifierRepository
Expand All @@ -39,13 +40,15 @@ import javax.inject.Inject
class SettingsViewModel @Inject constructor(
private val configRepository: ConfigRepository,
private val verifierRepository: VerifierRepository,
private val preferences: Preferences
private val preferences: Preferences,
private val getRevocationDataUseCase: GetRevocationDataUseCase
) : ViewModel(), LifecycleObserver {

private val _inProgress = MutableLiveData<Boolean>()
val inProgress: LiveData<Boolean> = _inProgress

val lastSyncLiveData: LiveData<Long> = verifierRepository.getLastSyncTimeMillis()
val lastSyncLiveData: LiveData<Long> = verifierRepository.getLastPubKeysSyncTimeMillis()
val lastRevocationSyncTime = MutableLiveData(verifierRepository.getLastRevocationSyncTimeMillis())

private val _debugModeState: MutableLiveData<DebugModeState> = MutableLiveData(DebugModeState.OFF)
val debugModeState: LiveData<DebugModeState> = _debugModeState
Expand Down Expand Up @@ -78,4 +81,14 @@ class SettingsViewModel @Inject constructor(
_inProgress.value = false
}
}

fun syncRevocation() {
_inProgress.value = true
getRevocationDataUseCase.execute(
viewModelScope,
onFailure = { Timber.d("error refreshing revocation: $it") },
onSuccess = { lastRevocationSyncTime.value = verifierRepository.getLastRevocationSyncTimeMillis() },
onComplete = { _inProgress.value = false }
)
}
}
33 changes: 31 additions & 2 deletions app/src/main/res/layout/fragment_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
android:letterSpacing="0"
android:minHeight="48dp"
android:paddingHorizontal="@dimen/default_padding"
android:text="@string/reload"
android:text="@string/reload_pub_keys"
android:textAllCaps="false"
app:layout_constraintTop_toBottomOf="@+id/trustedPublicKeys" />

Expand All @@ -129,6 +129,35 @@
tools:layout_editor_absoluteX="16dp"
tools:text="Last Updated: 2021-05-27 09:44" />

<com.google.android.material.button.MaterialButton
android:id="@+id/syncRevocation"
style="@style/TextAppearance.Dgca.SettingsButtonHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_synchronize_solid"
android:gravity="start|center"
android:insetTop="0dp"
android:insetBottom="0dp"
android:letterSpacing="0"
android:minHeight="48dp"
android:paddingHorizontal="@dimen/default_padding"
android:text="@string/reload_revocation"
android:textAllCaps="false"
app:layout_constraintTop_toBottomOf="@+id/lastUpdate" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/lastRevocationUpdate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/default_margin"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/grey_50"
android:textSize="@dimen/text_size_medium"
app:layout_constraintTop_toBottomOf="@+id/syncRevocation"
tools:layout_editor_absoluteX="16dp"
tools:text="Last Updated: 2021-05-27 09:44" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/materialTextView"
android:layout_width="match_parent"
Expand All @@ -138,7 +167,7 @@
android:text="@string/settings_version_title"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@+id/lastUpdate"
app:layout_constraintTop_toBottomOf="@+id/lastRevocationUpdate"
tools:layout_editor_absoluteX="0dp" />

<com.google.android.material.textview.MaterialTextView
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-nl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<string name="privacy_information">Privacy Informatie</string>
<string name="licenses">Licenties</string>
<string name="trusted_public_keys">Vertrouwde openbare sleutels</string>
<string name="reload">Herladen</string>
<string name="reload_pub_keys">Herladen</string>
<string name="last_updated">Laatst bijgewerkt: %s</string>
<string name="version">Versie: %1$s (%2$d)</string>
<string name="about">Over</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
<string name="privacy_information">Privacy information</string>
<string name="licenses">Licenses</string>
<string name="trusted_public_keys">Trusted public keys</string>
<string name="reload">Reload</string>
<string name="reload_pub_keys">Reload pub keys</string>
<string name="reload_revocation">Reload revocation list</string>
<string name="last_updated">Last Updated: %s</string>
<string name="version">Version: %1$s (%2$d)</string>
<string name="about">About</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ interface RevocationPreferences {

var eTag: String?

var lastRevocationSyncTimeMillis: Long

fun clear()
}

Expand All @@ -50,13 +52,20 @@ class RevocationPreferencesImpl(context: Context) : RevocationPreferences {
KEY_ETAG
)

override var lastRevocationSyncTimeMillis by LongPreference(
preferences,
KEY_REVOCATION_SYNC_TIME_MILLIS,
-1
)

override fun clear() {
preferences.value.edit().clear().apply()
}

companion object {
private const val USER_PREF = "dcc.revocation.app.pref"
private const val KEY_ETAG = "dcc.revocation.app.pref.eTag"
private const val KEY_REVOCATION_SYNC_TIME_MILLIS = "dcc.revocation.app.pref.last_revocation_sync_time_millis"
}
}

Expand All @@ -74,4 +83,20 @@ class StringPreference(
override fun setValue(thisRef: Any, property: KProperty<*>, value: String?) {
preferences.value.edit { putString(name, value) }
}
}

class LongPreference(
private val preferences: Lazy<SharedPreferences>,
private val name: String,
private val defaultValue: Long
) : ReadWriteProperty<Any, Long> {

@WorkerThread
override fun getValue(thisRef: Any, property: KProperty<*>): Long {
return preferences.value.getLong(name, defaultValue)
}

override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) {
preferences.value.edit { putLong(name, value) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package dcc.app.revocation.domain.usacase

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import dcc.app.revocation.data.RevocationPreferences
import dcc.app.revocation.data.network.model.RevocationPartitionResponse
import dcc.app.revocation.data.network.model.Slice
import dcc.app.revocation.data.network.model.SliceType
Expand All @@ -41,13 +42,15 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import java.io.InputStream
import java.lang.reflect.Type
import java.time.Instant
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.util.zip.GZIPInputStream
import javax.inject.Inject


class GetRevocationDataUseCase @Inject constructor(
private val repository: RevocationRepository,
private val revocationPreferences: RevocationPreferences,
dispatcher: CoroutineDispatcher,
errorHandler: ErrorHandler,
) : BaseUseCase<Unit, Any>(dispatcher, errorHandler) {
Expand All @@ -66,7 +69,9 @@ class GetRevocationDataUseCase @Inject constructor(
}

// Delete expired data
repository.deleteExpiredData(System.currentTimeMillis())
repository.deleteExpiredData(ChronoUnit.MICROS.between(Instant.EPOCH, ZonedDateTime.now().toInstant()))

revocationPreferences.lastRevocationSyncTimeMillis = System.currentTimeMillis()
}

private suspend fun checkKidMetadata(revocationKidData: RevocationKidData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ import dcc.app.revocation.data.network.mapper.toRevocationKidData
import dcc.app.revocation.data.network.model.RevocationPartitionResponse
import dcc.app.revocation.data.network.model.SliceType
import dcc.app.revocation.domain.RevocationRepository
import dcc.app.revocation.domain.model.*
import dcc.app.revocation.domain.model.DccRevocationKidMetadata
import dcc.app.revocation.domain.model.DccRevocationPartition
import dcc.app.revocation.domain.model.DccRevocationSlice
import dcc.app.revocation.domain.model.RevocationKidData
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.net.HttpURLConnection
import javax.inject.Inject

@Suppress("BlockingMethodInNonBlockingContext")
Expand All @@ -50,9 +54,13 @@ class RevocationRepositoryImpl @Inject constructor(
if (response.containsServerError()) {
throw HttpException(response)
}
revocationPreferences.eTag = response.headers()["eTag"]?.replace("\"", "")

return response.body()?.map { it.toRevocationKidData() } ?: emptyList()
return if (response.code() == HttpURLConnection.HTTP_OK) {
revocationPreferences.eTag = response.headers()["eTag"]?.replace("\"", "")
response.body()?.map { it.toRevocationKidData() } ?: emptyList()
} else {
emptyList()
}
}

@Throws(Exception::class)
Expand Down

0 comments on commit e99e5d8

Please sign in to comment.