Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
515ce19
feat: support Pass-Secrets mapped names
pando85 Sep 5, 2026
73f7139
style: fix manifest formatting
pando85 Sep 5, 2026
574a7ee
test: clean up Pass-Secrets coverage
pando85 Sep 5, 2026
90b8783
refactor: harden Pass-Secrets map resolution
pando85 Sep 5, 2026
bb4873d
test: cover Pass-Secrets activation and fallback cases
pando85 Sep 5, 2026
ffffcf8
fix: keep mapped labels out of persisted intents
pando85 Sep 5, 2026
c46d1c7
fix: keep Pass-Secrets labels out of launcher shortcuts
pando85 Sep 5, 2026
f5b5aef
style: format PasswordItem
pando85 Sep 5, 2026
5c90a7e
style: format Pass-Secrets resolver
pando85 Sep 5, 2026
978f848
style: format Pass-Secrets tests
pando85 Sep 5, 2026
4ed8b44
style: format repository view model
pando85 Sep 5, 2026
e273eae
feat: complete Pass-Secrets metadata model
pando85 Sep 5, 2026
d6eab2e
feat: persist Pass-Secrets metadata atomically
pando85 Sep 5, 2026
abce869
feat: unlock Pass-Secrets aliases with mappings
pando85 Sep 5, 2026
93abc9a
feat: search Pass-Secrets mask aliases
pando85 Sep 5, 2026
4c6f178
feat: include Pass-Secrets aliases in search
pando85 Sep 5, 2026
d78a44f
feat: edit Pass-Secrets entries by logical name
pando85 Sep 5, 2026
b4876de
fix: disambiguate Pass-Secrets metadata versions
pando85 Sep 5, 2026
c072611
feat: make Pass-Secrets password writes transactional
pando85 Sep 5, 2026
8b004b8
feat: stage Pass-Secrets deletes and moves safely
pando85 Sep 5, 2026
2a896ff
feat: make password create and edit Pass-Secrets aware
pando85 Sep 5, 2026
971fec3
feat: support transactional batch Pass-Secrets moves
pando85 Sep 5, 2026
4963989
feat: add Pass-Secrets mutation errors
pando85 Sep 5, 2026
0ec710e
feat: make move delete and rename Pass-Secrets aware
pando85 Sep 5, 2026
e3872a2
fix: resolve substringBefore Regex compilation error and apply format…
Sep 5, 2026
8c78424
fix: replace runCatching with try-catch in PassSecretsMapWriter
Sep 5, 2026
d371515
fix: suppress DiffUtilEquals lint warning for List comparison
Sep 5, 2026
11e47fb
fix: add lazy messages to requireNotNull calls in PassSecretsMutation…
Sep 5, 2026
e126f95
fix: add lazy messages to requireNotNull calls in PasswordStore
Sep 5, 2026
76d0e26
fix: remove unused password_move_error string resources
Sep 5, 2026
2e3e654
fix: remove unused password_creation_duplicate_error string resource
Sep 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
android:name=".ui.crypto.DecryptActivity"
android:exported="true" />

<activity
android:name=".ui.crypto.PassSecretsMapUnlockActivity"
android:excludeFromRecents="true"
android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />

<activity
android:name=".ui.main.LaunchActivity"
android:configChanges="orientation|screenSize"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/app/passwordstore/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.appcompat.app.AppCompatDelegate
import app.passwordstore.injection.context.FilesDirPath
import app.passwordstore.injection.prefs.PGPPassphrases
import app.passwordstore.injection.prefs.SettingsPreferences
import app.passwordstore.passsecrets.PassSecretsMapStore
import app.passwordstore.ui.crypto.BasePGPActivity.Companion.cachedPassphrases
import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.crypto.AESEncryption
Expand Down Expand Up @@ -154,6 +155,7 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
if (intent.action == Intent.ACTION_SCREEN_OFF) {
cachedPassphrases.values.forEach { it.wipe() }
cachedPassphrases.clear()
PassSecretsMapStore.clear()
}
}
}
Expand Down
64 changes: 57 additions & 7 deletions app/src/main/java/app/passwordstore/data/password/PasswordItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,57 @@ data class PasswordItem(
val type: Char,
val file: File,
val rootDir: File,
val mappedName: String? = null,
val aliases: List<String> = emptyList(),
) : Comparable<PasswordItem> {

val physicalName = name.replace("\\.gpg$".toRegex(), "")

val fullPathToParent = PasswordRepository.getParentPath(file.absolutePath, rootDir.absolutePath)

val physicalLongName =
PasswordRepository.getLongName(fullPathToParent, rootDir.absolutePath, physicalName)

val longName = PasswordRepository.getLongName(fullPathToParent, rootDir.absolutePath, toString())

val searchableName = buildList {
add(physicalLongName)
if (mappedName != null) add(longName)
addAll(aliases)
}
.joinToString(" ")

fun matchesSearch(filter: String): Boolean = searchableName.contains(filter, ignoreCase = true)

fun matchesStrictDomain(regex: Regex): Boolean {
val physicalPath =
try {
file.relativeTo(rootDir).path
} catch (_: IllegalArgumentException) {
return false
}
if (regex.containsMatchIn(physicalPath)) return true

val logicalTokens = buildList {
mappedName?.split(Regex("[\\s/]+"))?.filterTo(this) { it.isNotBlank() }
aliases.forEach { alias ->
add(alias)
if ('@' in alias) add(alias.substringAfterLast('@'))
}
}
return logicalTokens.any { token -> regex.containsMatchIn("$token.gpg") }
}

override fun equals(other: Any?): Boolean {
return (other is PasswordItem) && (other.file == file)
}

override fun compareTo(other: PasswordItem): Int {
return (type + name).compareTo(other.type + other.name, ignoreCase = true)
return (type + toString()).compareTo(other.type + other.toString(), ignoreCase = true)
}

override fun toString(): String {
return name.replace("\\.gpg$".toRegex(), "")
return mappedName ?: physicalName
}

override fun hashCode(): Int {
Expand All @@ -42,7 +77,9 @@ data class PasswordItem(
/** Creates an [Intent] to launch this [PasswordItem] through the authentication process. */
fun createAuthEnabledIntent(context: Context): Intent {
val intent = Intent(context, LaunchActivity::class.java)
intent.putExtra("NAME", toString()) // this.toString
// Intent extras may outlive the current UI process through Android shortcuts. Keep the
// persisted identity physical even when a Pass-Secrets label is currently unlocked.
intent.putExtra("NAME", physicalName)
intent.putExtra(BasePGPActivity.EXTRA_FILE_PATH, file.absolutePath)
intent.putExtra(
BasePGPActivity.EXTRA_REPO_PATH,
Expand Down Expand Up @@ -70,13 +107,26 @@ data class PasswordItem(
}

@JvmStatic
fun newPassword(name: String, file: File, parent: PasswordItem, rootDir: File): PasswordItem {
return PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir)
fun newPassword(
name: String,
file: File,
parent: PasswordItem,
rootDir: File,
mappedName: String? = null,
aliases: List<String> = emptyList(),
): PasswordItem {
return PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir, mappedName, aliases)
}

@JvmStatic
fun newPassword(name: String, file: File, rootDir: File): PasswordItem {
return PasswordItem(name, null, TYPE_PASSWORD, file, rootDir)
fun newPassword(
name: String,
file: File,
rootDir: File,
mappedName: String? = null,
aliases: List<String> = emptyList(),
): PasswordItem {
return PasswordItem(name, null, TYPE_PASSWORD, file, rootDir, mappedName, aliases)
}

@JvmStatic
Expand Down
Loading