mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Trust extension by repo (#570)
This commit is contained in:
parent
21145144cd
commit
70cd688ac2
6 changed files with 35 additions and 27 deletions
|
@ -179,7 +179,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
addFactory { TrustExtension(get()) }
|
addFactory { TrustExtension(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||||
addFactory { ExtensionRepoService(get(), get()) }
|
addFactory { ExtensionRepoService(get(), get()) }
|
||||||
|
|
|
@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
import tachiyomi.core.common.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
class TrustExtension(
|
class TrustExtension(
|
||||||
|
private val extensionRepoRepository: ExtensionRepoRepository,
|
||||||
private val preferences: SourcePreferences,
|
private val preferences: SourcePreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean {
|
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
|
||||||
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash"
|
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
|
||||||
return key in preferences.trustedExtensions().get()
|
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
|
||||||
|
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
||||||
|
@ -19,9 +22,7 @@ class TrustExtension(
|
||||||
// Remove previously trusted versions
|
// Remove previously trusted versions
|
||||||
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
||||||
|
|
||||||
removed.also {
|
removed.also { it += "$pkgName:$versionCode:$signatureHash" }
|
||||||
it += "$pkgName:$versionCode:$signatureHash"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ class ExtensionManager(
|
||||||
*
|
*
|
||||||
* @param extension the extension to trust
|
* @param extension the extension to trust
|
||||||
*/
|
*/
|
||||||
fun trust(extension: Extension.Untrusted) {
|
suspend fun trust(extension: Extension.Untrusted) {
|
||||||
_untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
|
_untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
|
||||||
|
|
||||||
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
||||||
|
|
|
@ -9,6 +9,9 @@ import androidx.core.content.ContextCompat
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
|
@ -20,16 +23,12 @@ import tachiyomi.core.common.util.system.logcat
|
||||||
*/
|
*/
|
||||||
internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() {
|
internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() {
|
||||||
|
|
||||||
/**
|
val scope = CoroutineScope(SupervisorJob())
|
||||||
* Registers this broadcast receiver
|
|
||||||
*/
|
|
||||||
fun register(context: Context) {
|
fun register(context: Context) {
|
||||||
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the intent filter this receiver should subscribe to.
|
|
||||||
*/
|
|
||||||
private val filter = IntentFilter().apply {
|
private val filter = IntentFilter().apply {
|
||||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||||
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||||
|
@ -51,19 +50,23 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc
|
||||||
Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> {
|
Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> {
|
||||||
if (isReplacing(intent)) return
|
if (isReplacing(intent)) return
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
is LoadResult.Success -> listener.onExtensionInstalled(result.extension)
|
is LoadResult.Success -> listener.onExtensionInstalled(result.extension)
|
||||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> {
|
Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> {
|
||||||
|
scope.launch {
|
||||||
when (val result = getExtensionFromIntent(context, intent)) {
|
when (val result = getExtensionFromIntent(context, intent)) {
|
||||||
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
||||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> {
|
Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> {
|
||||||
if (isReplacing(intent)) return
|
if (isReplacing(intent)) return
|
||||||
|
|
||||||
|
@ -90,7 +93,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param intent The intent containing the package name of the extension.
|
* @param intent The intent containing the package name of the extension.
|
||||||
*/
|
*/
|
||||||
private fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult {
|
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult {
|
||||||
val pkgName = getPackageNameFromIntent(intent)
|
val pkgName = getPackageNameFromIntent(intent)
|
||||||
if (pkgName == null) {
|
if (pkgName == null) {
|
||||||
logcat(LogPriority.WARN) { "Package name not found" }
|
logcat(LogPriority.WARN) { "Package name not found" }
|
||||||
|
|
|
@ -172,7 +172,7 @@ internal object ExtensionLoader {
|
||||||
* Attempts to load an extension from the given package name. It checks if the extension
|
* Attempts to load an extension from the given package name. It checks if the extension
|
||||||
* contains the required feature flag before trying to load it.
|
* contains the required feature flag before trying to load it.
|
||||||
*/
|
*/
|
||||||
fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
|
suspend fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
|
||||||
val extensionPackage = getExtensionInfoFromPkgName(context, pkgName)
|
val extensionPackage = getExtensionInfoFromPkgName(context, pkgName)
|
||||||
if (extensionPackage == null) {
|
if (extensionPackage == null) {
|
||||||
logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" }
|
logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" }
|
||||||
|
@ -223,7 +223,8 @@ internal object ExtensionLoader {
|
||||||
* @param context The application context.
|
* @param context The application context.
|
||||||
* @param extensionInfo The extension to load.
|
* @param extensionInfo The extension to load.
|
||||||
*/
|
*/
|
||||||
private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult {
|
@Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount")
|
||||||
|
private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult {
|
||||||
val pkgManager = context.packageManager
|
val pkgManager = context.packageManager
|
||||||
val pkgInfo = extensionInfo.packageInfo
|
val pkgInfo = extensionInfo.packageInfo
|
||||||
val appInfo = pkgInfo.applicationInfo
|
val appInfo = pkgInfo.applicationInfo
|
||||||
|
@ -252,7 +253,7 @@ internal object ExtensionLoader {
|
||||||
if (signatures.isNullOrEmpty()) {
|
if (signatures.isNullOrEmpty()) {
|
||||||
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
|
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
|
||||||
return LoadResult.Error
|
return LoadResult.Error
|
||||||
} else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) {
|
} else if (!trustExtension.isTrusted(pkgInfo, signatures)) {
|
||||||
val extension = Extension.Untrusted(
|
val extension = Extension.Untrusted(
|
||||||
extName,
|
extName,
|
||||||
pkgName,
|
pkgName,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onCompletion
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -196,8 +197,10 @@ class ExtensionsScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trustExtension(extension: Extension.Untrusted) {
|
fun trustExtension(extension: Extension.Untrusted) {
|
||||||
|
screenModelScope.launch {
|
||||||
extensionManager.trust(extension)
|
extensionManager.trust(extension)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class State(
|
data class State(
|
||||||
|
|
Loading…
Reference in a new issue