Skip to content

Commit

Permalink
Add OTP url handling to SetupOneTimePassword screen
Browse files Browse the repository at this point in the history
  • Loading branch information
aivanovski committed Mar 2, 2024
1 parent df6d8e6 commit 1bee6dc
Show file tree
Hide file tree
Showing 17 changed files with 705 additions and 162 deletions.
8 changes: 6 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,16 @@ android {

def koinVersion = '3.5.0'
def roomVersion = '2.6.1'
def lifecycleVersion = '2.2.0'
def lifecycleExtensionVersion = '2.2.0'
def lifecycleVersion = '2.7.0'
def appCompatVersion = '1.6.1'
def androidAnnotationVersion = '1.7.0'
def multidexVersion = '2.0.1'
def coroutinesVersion = '1.7.3'
def recyclerViewVersion = '1.3.2'
def materialVersion = '1.9.0'
def constrainLayoutVersion = '2.1.4'
def constraintComposeVersion = '1.0.1'
def cardViewVersion = '1.0.0'
def coreKtxVersion = '1.10.1'
def activityKtxVersion = '1.7.2'
Expand Down Expand Up @@ -250,7 +252,7 @@ dependencies {
implementation "androidx.annotation:annotation:$androidAnnotationVersion"
implementation "androidx.constraintlayout:constraintlayout:$constrainLayoutVersion"
implementation "androidx.biometric:biometric:$biometricVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionVersion"
implementation "androidx.core:core-ktx:$coreKtxVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$liveDataKtxVersion"
implementation "androidx.activity:activity-ktx:$activityKtxVersion"
Expand All @@ -260,6 +262,8 @@ dependencies {
implementation platform("androidx.compose:compose-bom:2024.02.00")
implementation "androidx.compose.material3:material3"
implementation "androidx.activity:activity-compose"
implementation "androidx.constraintlayout:constraintlayout-compose:$constraintComposeVersion"
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycleVersion"

// Compose preview
implementation "androidx.compose.ui:ui-tooling-preview"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ivanovsky.passnotes.domain.otp

import com.ivanovsky.passnotes.domain.otp.model.OtpToken
import com.ivanovsky.passnotes.util.removeSpaces

object OtpParametersValidator {

Expand All @@ -17,6 +18,6 @@ object OtpParametersValidator {
}

fun isSecretValid(secret: String?): Boolean {
return secret != null && secret.trim().isNotEmpty()
return secret != null && secret.removeSpaces().isNotEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.ivanovsky.passnotes.util.StringUtils.QUESTION_MARK
import com.ivanovsky.passnotes.util.StringUtils.SLASH
import com.ivanovsky.passnotes.util.toIntSafely
import com.ivanovsky.passnotes.util.toLongSafely
import java.util.regex.Pattern

object OtpUriFactory {

Expand All @@ -29,6 +30,8 @@ object OtpUriFactory {
private const val URL_PARAM_COUNTER = "counter"
private const val URL_PARAM_ALGORITHM = "algorithm"

private val OTP_URI_PATTERN = Pattern.compile("otpauth://[th]otp[\\/?][a-zA-Z0-9\\/?&=:%\\.]+")

fun createUri(token: OtpToken): String {
return StringBuilder()
.apply {
Expand Down Expand Up @@ -68,8 +71,8 @@ object OtpUriFactory {
.toString()
}

fun parseUri(otpUri: String): OtpToken? {
if (otpUri.isBlank()) {
fun parseUri(text: String): OtpToken? {
if (text.isBlank()) {
return null
}

Expand All @@ -80,7 +83,12 @@ object OtpUriFactory {
var period: Int? = null
var algorithm: HashAlgorithmType? = null

val uri = Uri.parse(otpUri.trim())
val trimmedText = text.trim()
if (!OTP_URI_PATTERN.matcher(trimmedText).matches()) {
return null
}

val uri = Uri.parse(trimmedText)
if (uri.scheme?.lowercase() != SCHEME) {
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ enum class HashAlgorithmType(val rfcName: String) {
companion object {

fun fromString(name: String): HashAlgorithmType? {
val loweredName = name.lowercase()
return entries.firstOrNull { algorithm -> loweredName == algorithm.name }
return entries.firstOrNull { algorithm ->
name.equals(
algorithm.name,
ignoreCase = true
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ data class AppColors(
val importantIcon: Color,
val diffInsert: Color,
val diffDelete: Color,
val diffUpdate: Color
val diffUpdate: Color,
val progress: Color,
val progressSecondary: Color
)

val LightAppColors = AppColors(
Expand Down Expand Up @@ -59,7 +61,9 @@ val LightAppColors = AppColors(
importantIcon = Color(0xFFEF5350),
diffInsert = Color(0xFFBEFFBB),
diffDelete = Color(0xFFFFD5D5),
diffUpdate = Color(0xFFF0EFAA)
diffUpdate = Color(0xFFF0EFAA),
progress = Color(0xFF3F51B5),
progressSecondary = Color(0xFF3F51B5),
)

val DarkAppColors = AppColors(
Expand Down Expand Up @@ -88,5 +92,7 @@ val DarkAppColors = AppColors(
importantIcon = Color(0xFF690005),
diffInsert = Color(0xFF061E0B),
diffDelete = Color(0xFF300406),
diffUpdate = Color(0xFF33331B)
diffUpdate = Color(0xFF33331B),
progress = Color(0xFF2E3856),
progressSecondary = Color(0xFFEADDFF),
)
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class NoteEditorCellModelFactory(
createUserNameCell(EMPTY),
createPasswordCell(EMPTY),
createUrlCell(EMPTY),
createNotesCell(EMPTY)
createNotesCell(EMPTY),
createSpaceCell()
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class SetupOneTimePasswordFragment : FragmentWithDoneButton() {
viewModel.navigateBack()
true
}

else -> {
super.onOptionsItemSelected(item)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.ivanovsky.passnotes.presentation.setupOneTimePassword

import com.ivanovsky.passnotes.domain.otp.OtpParametersValidator
import com.ivanovsky.passnotes.domain.otp.OtpUriFactory
import com.ivanovsky.passnotes.util.StringUtils.EMPTY

class SetupOneTimePasswordInteractor {

Expand All @@ -19,4 +21,8 @@ class SetupOneTimePasswordInteractor {
fun isSecretValid(secret: String?): Boolean {
return OtpParametersValidator.isSecretValid(secret)
}

fun isUrlValid(url: String?): Boolean {
return OtpUriFactory.parseUri(url ?: EMPTY) != null
}
}
Loading

0 comments on commit 1bee6dc

Please sign in to comment.