diff --git a/src/main/java/com/github/ai/kpdiff/domain/MainInteractor.kt b/src/main/java/com/github/ai/kpdiff/domain/MainInteractor.kt index e3d3813..9c982eb 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/MainInteractor.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/MainInteractor.kt @@ -75,7 +75,8 @@ class MainInteractor( } else { val options = DiffFormatterOptions( isColorEnabled = !parsedArgs.isNoColoredOutput, - isVerboseOutput = parsedArgs.isVerboseOutput + isVerboseOutput = parsedArgs.isVerboseOutput, + isReveal = parsedArgs.isReveal ) printDiffUseCase.printDiff( diff --git a/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentParser.kt b/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentParser.kt index 88abd15..60ed843 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentParser.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentParser.kt @@ -96,6 +96,7 @@ class ArgumentParser( OptionalArgument.HELP -> parseHelp(values) OptionalArgument.VERSION -> parseVersion(values) OptionalArgument.VERBOSE -> parseVerbose(values) + OptionalArgument.REVEAL -> parseReveal(values) OptionalArgument.KEY_FILE_A -> parseLeftKeyPath(queue.poll(), values) OptionalArgument.KEY_FILE_B -> parseRightKeyPath(queue.poll(), values) OptionalArgument.PASSWORD -> parsePassword(queue.poll(), values) @@ -152,6 +153,11 @@ class ArgumentParser( return Either.Right(Unit) } + private fun parseReveal(arguments: MutableArguments): Either { + arguments.isReveal = true + return Either.Right(Unit) + } + private fun parseLeftKeyPath( path: String?, arguments: MutableArguments diff --git a/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentsExtensions.kt b/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentsExtensions.kt index 6353446..6a08672 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentsExtensions.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/argument/ArgumentsExtensions.kt @@ -23,6 +23,7 @@ fun MutableArguments.toArguments(): Arguments { isNoColoredOutput = isNoColoredOutput, isPrintHelp = isPrintHelp, isPrintVersion = isPrintVersion, - isVerboseOutput = isVerboseOutput + isVerboseOutput = isVerboseOutput, + isReveal = isReveal ) } \ No newline at end of file diff --git a/src/main/java/com/github/ai/kpdiff/domain/argument/OptionalArgument.kt b/src/main/java/com/github/ai/kpdiff/domain/argument/OptionalArgument.kt index 97d258f..56765a2 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/argument/OptionalArgument.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/argument/OptionalArgument.kt @@ -22,7 +22,8 @@ enum class OptionalArgument( PASSWORD_B(shortName = null, fullName = "password-b"), VERBOSE(shortName = "v", fullName = "verbose"), DIFF_BY(shortName = "d", fullName = "diff-by"), - OUTPUT_FILE(shortName = "f", fullName = "output-file"); + OUTPUT_FILE(shortName = "f", fullName = "output-file"), + REVEAL(shortName = null, fullName = "reveal"); val cliShortName: String? = if (shortName != null) { "-$shortName" diff --git a/src/main/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImpl.kt b/src/main/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImpl.kt index 807e8c4..501916d 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImpl.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImpl.kt @@ -194,8 +194,10 @@ class DiffFormatterImpl( val formatter = formatterProvider.getFormatter(entity::class) as EntityFormatter + val formattedEvent = event.maskProtectedFieldsUnlessRevealed(options) + return terminalOutputFormatter.format( - line = formatter.format(event as DiffEvent, indent), + line = formatter.format(formattedEvent as DiffEvent, indent), color = if (options.isColorEnabled) { event.getColor() } else { @@ -204,6 +206,46 @@ class DiffFormatterImpl( ) } + @Suppress("UNCHECKED_CAST") + private fun DiffEvent.maskProtectedFieldsUnlessRevealed( + options: DiffFormatterOptions + ): DiffEvent { + if (options.isReveal) { + return this + } + + return when (this) { + is DiffEvent.Insert -> DiffEvent.Insert( + parentUuid = parentUuid, + entity = entity.maskProtectedField() + ) as DiffEvent + + is DiffEvent.Delete -> DiffEvent.Delete( + parentUuid = parentUuid, + entity = entity.maskProtectedField() + ) as DiffEvent + + is DiffEvent.Update -> DiffEvent.Update( + oldParentUuid = oldParentUuid, + newParentUuid = newParentUuid, + oldEntity = oldEntity.maskProtectedField(), + newEntity = newEntity.maskProtectedField() + ) as DiffEvent + } + } + + private fun T.maskProtectedField(): T { + if (this !is Field<*> || name != Fields.FIELD_PASSWORD) { + return this + } + + return Field( + uuid = uuid, + name = name, + value = PROTECTED_FIELD_PLACEHOLDER + ) as T + } + private fun shouldPrintAdditionalInformation( event: DiffEvent<*>, options: DiffFormatterOptions @@ -276,6 +318,8 @@ class DiffFormatterImpl( companion object { private const val INDENT = " " + private const val PROTECTED_FIELD_PLACEHOLDER = "***" + private val DEFAULT_PROPERTIES = setOf( Fields.FIELD_TITLE, Fields.FIELD_USERNAME, diff --git a/src/main/java/com/github/ai/kpdiff/domain/usecases/PrintHelpUseCase.kt b/src/main/java/com/github/ai/kpdiff/domain/usecases/PrintHelpUseCase.kt index 8b32fa4..ad1c53f 100644 --- a/src/main/java/com/github/ai/kpdiff/domain/usecases/PrintHelpUseCase.kt +++ b/src/main/java/com/github/ai/kpdiff/domain/usecases/PrintHelpUseCase.kt @@ -43,6 +43,7 @@ class PrintHelpUseCase( -d, --diff-by Type of differ, default is 'path'. Possible values: path - produces more accurate diff, considers entries identical if they have identical content but UUID differs uuid - considers entries identical if they have identical content and UUID + --reveal Reveal protected field values such as passwords -v, --verbose Print verbose output (entry fields will be printed) -V, --version Print version -h, --help Print help information diff --git a/src/main/java/com/github/ai/kpdiff/entity/Arguments.kt b/src/main/java/com/github/ai/kpdiff/entity/Arguments.kt index 5957f48..4a0ca12 100644 --- a/src/main/java/com/github/ai/kpdiff/entity/Arguments.kt +++ b/src/main/java/com/github/ai/kpdiff/entity/Arguments.kt @@ -20,7 +20,8 @@ data class Arguments( val isNoColoredOutput: Boolean, val isPrintHelp: Boolean, val isPrintVersion: Boolean, - val isVerboseOutput: Boolean + val isVerboseOutput: Boolean, + val isReveal: Boolean ) { companion object { @@ -42,7 +43,8 @@ data class Arguments( isNoColoredOutput = false, isPrintHelp = false, isPrintVersion = false, - isVerboseOutput = false + isVerboseOutput = false, + isReveal = false ) } -} \ No newline at end of file +} diff --git a/src/main/java/com/github/ai/kpdiff/entity/DiffFormatterOptions.kt b/src/main/java/com/github/ai/kpdiff/entity/DiffFormatterOptions.kt index a7bc8a4..3bd91b0 100644 --- a/src/main/java/com/github/ai/kpdiff/entity/DiffFormatterOptions.kt +++ b/src/main/java/com/github/ai/kpdiff/entity/DiffFormatterOptions.kt @@ -2,5 +2,6 @@ package com.github.ai.kpdiff.entity data class DiffFormatterOptions( val isColorEnabled: Boolean = true, - val isVerboseOutput: Boolean = false -) \ No newline at end of file + val isVerboseOutput: Boolean = false, + val isReveal: Boolean = false +) diff --git a/src/main/java/com/github/ai/kpdiff/entity/MutableArguments.kt b/src/main/java/com/github/ai/kpdiff/entity/MutableArguments.kt index b129d8f..b33fa56 100644 --- a/src/main/java/com/github/ai/kpdiff/entity/MutableArguments.kt +++ b/src/main/java/com/github/ai/kpdiff/entity/MutableArguments.kt @@ -18,5 +18,6 @@ data class MutableArguments( var isNoColoredOutput: Boolean = false, var isPrintHelp: Boolean = false, var isPrintVersion: Boolean = false, - var isVerboseOutput: Boolean = false -) \ No newline at end of file + var isVerboseOutput: Boolean = false, + var isReveal: Boolean = false +) diff --git a/src/test/java/com/github/ai/kpdiff/domain/MainInteractorTest.kt b/src/test/java/com/github/ai/kpdiff/domain/MainInteractorTest.kt index 5d41524..d71184f 100644 --- a/src/test/java/com/github/ai/kpdiff/domain/MainInteractorTest.kt +++ b/src/test/java/com/github/ai/kpdiff/domain/MainInteractorTest.kt @@ -269,7 +269,8 @@ class MainInteractorTest { isNoColoredOutput = isNoColoredOutput, isPrintHelp = isPrintHelp, isPrintVersion = isPrintVersion, - isVerboseOutput = false + isVerboseOutput = false, + isReveal = false ) private fun newInteractor(): MainInteractor = diff --git a/src/test/java/com/github/ai/kpdiff/domain/argument/ArgumentParserTest.kt b/src/test/java/com/github/ai/kpdiff/domain/argument/ArgumentParserTest.kt index b5fb40e..2bae23a 100644 --- a/src/test/java/com/github/ai/kpdiff/domain/argument/ArgumentParserTest.kt +++ b/src/test/java/com/github/ai/kpdiff/domain/argument/ArgumentParserTest.kt @@ -204,6 +204,23 @@ internal class ArgumentParserTest { } } + + @Test + fun `parse should return arguments if --reveal is specified`() { + assertParsedSuccessfully( + arguments = arrayOf( + LEFT_FILE_PATH, + RIGHT_FILE_PATH, + OptionalArgument.REVEAL.cliFullName + ), + expectedArguments = newArguments( + LEFT_FILE_PATH, + RIGHT_FILE_PATH, + isReveal = true + ) + ) + } + @Test fun `parse should return arguments if --one-password is specified`() { listOf( @@ -674,7 +691,8 @@ internal class ArgumentParserTest { isNoColoredOutput: Boolean = false, isPrintHelp: Boolean = false, isPrintVersion: Boolean = false, - isVerboseOutput: Boolean = false + isVerboseOutput: Boolean = false, + isReveal: Boolean = false ): Arguments { return Arguments( leftPath = leftPath, @@ -694,7 +712,8 @@ internal class ArgumentParserTest { isNoColoredOutput = isNoColoredOutput, isPrintHelp = isPrintHelp, isPrintVersion = isPrintVersion, - isVerboseOutput = isVerboseOutput + isVerboseOutput = isVerboseOutput, + isReveal = isReveal ) } diff --git a/src/test/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImplTest.kt b/src/test/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImplTest.kt index 53c474c..670c9f5 100644 --- a/src/test/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImplTest.kt +++ b/src/test/java/com/github/ai/kpdiff/domain/diff/formatter/DiffFormatterImplTest.kt @@ -18,7 +18,8 @@ class DiffFormatterImplTest { fun `format should return diff between databases`() { listOf( Pair(defaultOptions(), newPathDiffer()) to OUTPUT, - Pair(verboseOptions(), newPathDiffer()) to VERBOSE_OUTPUT + Pair(verboseOptions(), newPathDiffer()) to VERBOSE_OUTPUT, + Pair(revealOptions(), newPathDiffer()) to REVEALED_VERBOSE_OUTPUT ).forEach { (data, expected) -> // arrange val (options, differ) = data @@ -53,6 +54,13 @@ class DiffFormatterImplTest { private fun verboseOptions(): DiffFormatterOptions = DiffFormatterOptions(isColorEnabled = false, isVerboseOutput = true) + private fun revealOptions(): DiffFormatterOptions = + DiffFormatterOptions( + isColorEnabled = false, + isVerboseOutput = true, + isReveal = true + ) + companion object { private val OUTPUT = """ ~ Group 'Database' @@ -97,13 +105,13 @@ class DiffFormatterImplTest { - Entry 'Github.com' - Field 'Title': 'Github.com' - Field 'UserName': 'john.doe@example.com' - - Field 'Password': 'abc123' + - Field 'Password': '***' - Field 'URL': 'https://github.com' - Field 'Notes': '' + Entry 'Gitlab' + Field 'Title': 'Gitlab' + Field 'UserName': 'john.doe@example.com' - + Field 'Password': 'abc123' + + Field 'Password': '***' + Field 'URL': 'https://gitlab.com' + Field 'Notes': '' ~ Group 'Database' @@ -116,11 +124,15 @@ class DiffFormatterImplTest { + Entry 'Facebook' + Field 'Title': 'Facebook' + Field 'UserName': 'john.doe@example.com' - + Field 'Password': 'abc123' + + Field 'Password': '***' + Field 'URL': 'https://facebook.com' + Field 'Notes': '' """.transformOutput() + private val REVEALED_VERBOSE_OUTPUT = VERBOSE_OUTPUT.map { line -> + line.replace("Field 'Password': '***'", "Field 'Password': 'abc123'") + } + private fun String.transformOutput(): List { return this .trimIndent() diff --git a/src/test/java/com/github/ai/kpdiff/domain/usecases/GetKeysUseCaseTest.kt b/src/test/java/com/github/ai/kpdiff/domain/usecases/GetKeysUseCaseTest.kt index 606fae3..e269492 100644 --- a/src/test/java/com/github/ai/kpdiff/domain/usecases/GetKeysUseCaseTest.kt +++ b/src/test/java/com/github/ai/kpdiff/domain/usecases/GetKeysUseCaseTest.kt @@ -344,7 +344,8 @@ class GetKeysUseCaseTest { isAskRightPassword = isAskRightPassword, isPrintHelp = false, isPrintVersion = false, - isVerboseOutput = false + isVerboseOutput = false, + isReveal = false ) } } \ No newline at end of file