mirror of
https://github.com/AChep/keyguard-app.git
synced 2025-02-02 15:26:53 +01:00
Add {otp} placeholder for generating TOTP codes
This commit is contained in:
parent
d06292c071
commit
458fbcdcc4
@ -1,6 +1,5 @@
|
||||
package com.artemchep.keyguard.common.service.placeholder
|
||||
|
||||
import arrow.core.Option
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
|
||||
// See:
|
||||
@ -9,6 +8,15 @@ interface Placeholder {
|
||||
operator fun get(key: String): IO<String?>?
|
||||
|
||||
interface Factory {
|
||||
|
||||
fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
): Placeholder?
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Placeholder.Factory>.create(
|
||||
scope: PlaceholderScope,
|
||||
): List<Placeholder> = this
|
||||
.mapNotNull { placeholder ->
|
||||
placeholder.createOrNull(scope)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.artemchep.keyguard.common.model.DSecret
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
data class PlaceholderScope(
|
||||
val cipher: DSecret,
|
||||
val now: Instant,
|
||||
val cipher: DSecret,
|
||||
val url: String? = null,
|
||||
)
|
||||
|
@ -2,34 +2,49 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.io.map
|
||||
import com.artemchep.keyguard.common.io.toIO
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.model.DSecret
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.usecase.GetTotpCode
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import com.artemchep.keyguard.common.service.totp.TotpService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.datetime.Instant
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class CipherPlaceholder(
|
||||
// private val getTotpCode: GetTotpCode,
|
||||
private val totpService: TotpService,
|
||||
private val cipher: DSecret,
|
||||
private val now: Instant,
|
||||
) : Placeholder {
|
||||
override fun get(
|
||||
key: String,
|
||||
): IO<String?>? = when {
|
||||
key.equals("uuid", ignoreCase = true) ->
|
||||
cipher.service.remote?.id.let(::io)
|
||||
|
||||
key.equals("title", ignoreCase = true) ->
|
||||
cipher.name.let(::io)
|
||||
|
||||
key.equals("username", ignoreCase = true) ->
|
||||
cipher.login?.username.let(::io)
|
||||
|
||||
key.equals("password", ignoreCase = true) ->
|
||||
cipher.login?.password.let(::io)
|
||||
// key.equals("otp", ignoreCase = true) -> run {
|
||||
// val token = cipher.login?.totp?.token
|
||||
// ?: return@run null.let(::io)
|
||||
// getTotpCode(token)
|
||||
// .toIO()
|
||||
// .map { code -> code.code }
|
||||
// }
|
||||
|
||||
key.equals("otp", ignoreCase = true) -> run {
|
||||
val token = cipher.login?.totp?.token
|
||||
?: return@run null.let(::io)
|
||||
ioEffect(Dispatchers.Default) {
|
||||
totpService
|
||||
.generate(
|
||||
token = token,
|
||||
timestamp = now,
|
||||
)
|
||||
.code
|
||||
}
|
||||
}
|
||||
|
||||
key.equals("notes", ignoreCase = true) ->
|
||||
cipher.notes.let(::io)
|
||||
// extras
|
||||
@ -39,7 +54,21 @@ class CipherPlaceholder(
|
||||
else -> null
|
||||
}
|
||||
|
||||
class Factory {
|
||||
class Factory(
|
||||
private val totpService: TotpService,
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
totpService = directDI.instance(),
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = CipherPlaceholder(
|
||||
totpService = totpService,
|
||||
cipher = scope.cipher,
|
||||
now = scope.now,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import org.kodein.di.DirectDI
|
||||
|
||||
class CommentPlaceholder(
|
||||
) : Placeholder {
|
||||
@ -15,4 +17,16 @@ class CommentPlaceholder(
|
||||
// unknown
|
||||
else -> null
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = CommentPlaceholder()
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.model.DSecret
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import org.kodein.di.DirectDI
|
||||
|
||||
class CustomPlaceholder(
|
||||
private val cipher: DSecret,
|
||||
@ -26,4 +28,18 @@ class CustomPlaceholder(
|
||||
// unknown
|
||||
else -> null
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = CustomPlaceholder(
|
||||
cipher = scope.cipher,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,19 @@
|
||||
package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
|
||||
import arrow.core.None
|
||||
import arrow.core.Option
|
||||
import arrow.core.some
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import kotlinx.datetime.Clock
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toJavaLocalDateTime
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.kodein.di.DirectDI
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class DateTimePlaceholder(
|
||||
private val now: Instant,
|
||||
) : Placeholder {
|
||||
private val now = Clock.System.now()
|
||||
|
||||
private val localDateTime by lazy {
|
||||
val tz = TimeZone.currentSystemDefault()
|
||||
now.toLocalDateTime(tz)
|
||||
@ -105,4 +103,18 @@ class DateTimePlaceholder(
|
||||
// unknown
|
||||
else -> null
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = DateTimePlaceholder(
|
||||
now = scope.now,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import org.kodein.di.DirectDI
|
||||
|
||||
class EnvironmentPlaceholder(
|
||||
) : Placeholder {
|
||||
@ -22,4 +24,16 @@ class EnvironmentPlaceholder(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = EnvironmentPlaceholder()
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,11 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import com.artemchep.keyguard.common.service.placeholder.util.Parser
|
||||
import com.artemchep.keyguard.common.usecase.GetTotpCode
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class TextReplaceRegexPlaceholder(
|
||||
) : Placeholder {
|
||||
@ -31,4 +35,16 @@ class TextReplaceRegexPlaceholder(
|
||||
rg.replace(params.value, replacement)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = TextReplaceRegexPlaceholder()
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,10 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import com.artemchep.keyguard.common.service.placeholder.util.Parser
|
||||
import io.ktor.util.*
|
||||
import org.kodein.di.DirectDI
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import java.util.Locale
|
||||
@ -85,4 +87,16 @@ class TextTransformPlaceholder(
|
||||
): String {
|
||||
return URLDecoder.decode(value, "UTF-8")
|
||||
}
|
||||
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = TextTransformPlaceholder()
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package com.artemchep.keyguard.common.service.placeholder.impl
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import io.ktor.http.*
|
||||
import org.kodein.di.DirectDI
|
||||
|
||||
class UrlPlaceholder(
|
||||
private val url: String,
|
||||
@ -90,7 +92,15 @@ class UrlPlaceholder(
|
||||
else -> null
|
||||
}
|
||||
|
||||
class Factory {
|
||||
class Factory(
|
||||
) : Placeholder.Factory {
|
||||
constructor(
|
||||
directDI: DirectDI,
|
||||
) : this(
|
||||
)
|
||||
|
||||
override fun createOrNull(
|
||||
scope: PlaceholderScope,
|
||||
) = scope.url?.let(::UrlPlaceholder)
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ import com.artemchep.keyguard.common.service.execute.ExecuteCommand
|
||||
import com.artemchep.keyguard.common.service.extract.LinkInfoExtractor
|
||||
import com.artemchep.keyguard.common.service.extract.LinkInfoRegistry
|
||||
import com.artemchep.keyguard.common.service.passkey.PassKeyService
|
||||
import com.artemchep.keyguard.common.service.placeholder.Placeholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.PlaceholderScope
|
||||
import com.artemchep.keyguard.common.service.placeholder.create
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CipherPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CommentPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CustomPlaceholder
|
||||
@ -266,6 +269,7 @@ fun vaultViewScreenState(
|
||||
addCipherOpenedHistory = instance(),
|
||||
getJustDeleteMeByUrl = instance(),
|
||||
windowCoroutineScope = instance(),
|
||||
placeholderFactories = allInstances(),
|
||||
linkInfoExtractors = allInstances(),
|
||||
mode = mode,
|
||||
contentColor = contentColor,
|
||||
@ -340,6 +344,7 @@ fun vaultViewScreenState(
|
||||
addCipherOpenedHistory: AddCipherOpenedHistory,
|
||||
getJustDeleteMeByUrl: GetJustDeleteMeByUrl,
|
||||
windowCoroutineScope: WindowCoroutineScope,
|
||||
placeholderFactories: List<Placeholder.Factory>,
|
||||
linkInfoExtractors: List<LinkInfoExtractor<LinkInfo, LinkInfo>>,
|
||||
itemId: String,
|
||||
accountId: String,
|
||||
@ -490,15 +495,8 @@ fun vaultViewScreenState(
|
||||
val canEdit = canAddSecret && secretOrNull.canEdit() && !hasCanNotWriteCiphers
|
||||
val canDelete = canAddSecret && secretOrNull.canDelete() && !hasCanNotWriteCiphers
|
||||
|
||||
val placeholders = listOf(
|
||||
CipherPlaceholder(secretOrNull),
|
||||
CommentPlaceholder(),
|
||||
CustomPlaceholder(secretOrNull),
|
||||
DateTimePlaceholder(),
|
||||
TextReplaceRegexPlaceholder(),
|
||||
TextTransformPlaceholder(),
|
||||
EnvironmentPlaceholder(),
|
||||
)
|
||||
val now = Clock.System.now()
|
||||
|
||||
val extractors = LinkInfoRegistry(linkInfoExtractors)
|
||||
val cipherUris = secretOrNull
|
||||
.uris
|
||||
@ -516,16 +514,28 @@ fun vaultViewScreenState(
|
||||
}
|
||||
|
||||
else -> {
|
||||
val newUriPlaceholders = placeholderFactories
|
||||
.create(
|
||||
scope = PlaceholderScope(
|
||||
now = now,
|
||||
cipher = secretOrNull,
|
||||
),
|
||||
)
|
||||
val newUriString = kotlin.runCatching {
|
||||
uri.uri.placeholderFormat(placeholders)
|
||||
uri.uri.placeholderFormat(newUriPlaceholders)
|
||||
}.getOrElse { uri.uri }
|
||||
val newUri = uri.copy(uri = newUriString)
|
||||
|
||||
// Process URL overrides
|
||||
val urlOverridePlaceholders by lazy {
|
||||
placeholders + listOf(
|
||||
UrlPlaceholder(newUriString),
|
||||
)
|
||||
placeholderFactories
|
||||
.create(
|
||||
scope = PlaceholderScope(
|
||||
now = now,
|
||||
cipher = secretOrNull,
|
||||
url = newUriString,
|
||||
),
|
||||
)
|
||||
}
|
||||
val urlOverrideList = urlOverrides
|
||||
.filter { override ->
|
||||
|
@ -28,6 +28,14 @@ import com.artemchep.keyguard.common.service.license.impl.LicenseServiceImpl
|
||||
import com.artemchep.keyguard.common.service.logging.LogRepository
|
||||
import com.artemchep.keyguard.common.service.passkey.PassKeyService
|
||||
import com.artemchep.keyguard.common.service.passkey.impl.PassKeyServiceImpl
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CipherPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CommentPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.CustomPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.DateTimePlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.EnvironmentPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.TextReplaceRegexPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.TextTransformPlaceholder
|
||||
import com.artemchep.keyguard.common.service.placeholder.impl.UrlPlaceholder
|
||||
import com.artemchep.keyguard.common.service.relays.di.emailRelayDiModule
|
||||
import com.artemchep.keyguard.common.service.review.ReviewLog
|
||||
import com.artemchep.keyguard.common.service.review.impl.ReviewLogImpl
|
||||
@ -952,6 +960,46 @@ fun globalModuleJvm() = DI.Module(
|
||||
)
|
||||
}
|
||||
import(emailRelayDiModule())
|
||||
bindSingleton<CipherPlaceholder.Factory> {
|
||||
CipherPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<CommentPlaceholder.Factory> {
|
||||
CommentPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<CustomPlaceholder.Factory> {
|
||||
CustomPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<DateTimePlaceholder.Factory> {
|
||||
DateTimePlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<EnvironmentPlaceholder.Factory> {
|
||||
EnvironmentPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<TextReplaceRegexPlaceholder.Factory> {
|
||||
TextReplaceRegexPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<TextTransformPlaceholder.Factory> {
|
||||
TextTransformPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<UrlPlaceholder.Factory> {
|
||||
UrlPlaceholder.Factory(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<DeeplinkService> {
|
||||
DeeplinkServiceImpl(
|
||||
directDI = this,
|
||||
|
Loading…
x
Reference in New Issue
Block a user