Repository navigation
Store keyring secrets without extra encryption and migrate legacy values (#13687) - #17504
Shu-lin-Liu wants to merge 28 commits into
Conversation
|
Hey @Shu-lin-Liu! 👋 Thank you for contributing to JabRef! We have automated checks in place, based on which you will soon get feedback if any of them are failing. We also use Qodo for review assistance. It will update your pull request description with a review help and offer suggestions to improve the pull request. After all automated checks pass, a maintainer will also review your contribution. Once that happens, you can go through their comments in the "Files changed" tab and act on them, or reply to the conversation if you have further inputs. You can read about the whole pull request process in our contribution guide. Please ensure that your pull request is in line with our AI Usage Policy and make necessary disclosures. |
|
New location seems to be good. Then in V7 we might remove the encryption totally. |
|
Thanks for the clarification! I’ll follow the approach of using the new location and keep only the decryption functionality in ‘Password’ until it can be removed in v7. I also noticed a small unrelated issue during testing: if a proxy password is saved first and “Persist password between sessions” is enabled afterwards without changing the password, the password is not stored in the keyring. It seems that ‘bindToKeyring’ in ‘JabRefCliPreferences’ only updates the keyring when the password value changes. Would you prefer me to open a separate issue for this? |
…e new locations too
…asswordTest.java has also been updated with one test removed and another rewritten.
…-keyring-plaintext
Part 2- Write plaintext in the legacy API key migration
Part 3- Keep Password for decrypting legacy values only
Part 4 - Add migration tests, CHANGELOG entry and run final checks
Part2 fix 13687 keyring plaintext
Fixed test, all passing now!
|
Code review by qodo was updated up to the latest commit b0a4ae6 |
…int directive to ADR
|
Code review by qodo was updated up to the latest commit 29b504f |
|
Do not mark a PR as ready-for-review while the CI or the automatic review is running. They need to be completed and passed. |
| final String V5_9_FETCHER_CUSTOM_KEY_NAMES = "fetcherCustomKeyNames"; | ||
| final String V5_9_FETCHER_CUSTOM_KEYS = "fetcherCustomKeys"; |
There was a problem hiding this comment.
2. Api migration tests use uppercase locals 📘 Rule violation ⚙ Maintainability
The new migration tests name method-local variables V5_9_FETCHER_CUSTOM_KEY_NAMES and V5_9_FETCHER_CUSTOM_KEYS as though they were constants. These locals appear in each added migration test, so maintainers must distinguish inconsistent naming from the class-level constant convention when updating the tests.
Agent Prompt
## Issue description
The added migration tests use uppercase constant-style names for method-local variables, which violates the project's Java naming convention.
## Fix Focus Areas
- jabgui/src/test/java/org/jabref/migrations/GuiPreferencesMigrationsTest.java[290-291]
- jabgui/src/test/java/org/jabref/migrations/GuiPreferencesMigrationsTest.java[313-314]
## Recommended Fix
Rename the method-local variables to descriptive lowerCamelCase names, such as `v5_9FetcherCustomKeyNames` and `v5_9FetcherCustomKeys`, and update their usages in the affected tests.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 29b504f |
|
All CI checks have passed and all Qodo findings are resolved. However, each time I mark the PR as ready for review, Qodo starts a new review and posts “Qodo is busy working”, which makes the guard turn the PR back into a draft. Could a maintainer please mark it as ready for review? Thank you! |
Summary
This PR closes #13687 by removing the additional encryption applied to secrets before storing them in the system keyring.
Proxy passwords, GitHub tokens, and API keys are now stored directly in the system keyring without application-level encryption, using separate locations:
org.jabref.proxy/proxyorg.jabref.github/githuborg.jabref.apikeys/ fetcher nameTo maintain compatibility with existing values, the migration follows the new-location approach that koppor approved in this PR. When reading a secret, JabRef first checks the new location. If no value is found, it checks the legacy location, decrypts the existing value using
Password.decrypt(), stores the decrypted value in the new location, and removes the old entry.If a legacy value cannot be decrypted, JabRef logs a warning and removes the unreadable entry. Only that secret is lost; all other secrets are still read normally.
The
Passwordclass is now retained only for decrypting legacy values during migration. Itsencrypt()method has been removed, while the existing decryption algorithm and fixed IV remain for compatibility with previously encrypted values.The existing API key preference migration has also been updated to store values directly in the new keyring location.
Steps to test
PasswordTest: verifies that ciphertext encrypted by older JabRef versions can still be decrypted.GuiPreferencesMigrationsTest: verifies that API keys are stored directly in the new keyring location.KeyringMigrationTest: verifies reading from the new location, migrating legacy values, handling missing or undecryptable values, and deleting values from both locations.main, configure a proxy password in Preferences → Network and enable "Persist password between sessions".security find-generic-password -s org.jabref -a proxy -wprints the encrypted value.security find-generic-password -s org.jabref -a proxyreports that the item could not be found, andsecurity find-generic-password -s org.jabref.proxy -a proxy -wprints the password as plain text.security add-generic-password -s org.jabref -a proxy -w 'invalid'Not covered by tests yet
Related issues and pull requests
Closes #13687
AI usage
AI tool and model: Claude (claude-opus-5-5)
How AI was used:
I used Claude to explain the existing code and encryption concepts, troubleshoot development environment issues, analyse the issue requirements, and discuss possible migration approaches.
Claude also generated the code changes in both commits, including the modifications to
Password.javaand the regression test. Additionally, I used AI assistance to review the grammar and wording of this PR description.My review and verification:
I reviewed the AI-generated code line by line and made sure I understood the changes. I also ran the tests and manually verified the behaviour in JabRef. I take full responsibility for the submitted code.
AI Influence Level: Code: AIL4, PR description: AIL2
AI CHECKLIST.md walkthrough
Checked against the final diff to
main:JabRefCliPreferences.java,Password.java,PreferencesMigrations.java,PasswordTest.java,GuiPreferencesMigrationsTest.java, the newKeyringMigrationTest.java, andCHANGELOG.md.1. Code self-review
Nullability and control flow
== null/!= nullchecks — JSpecify annotations (@NullMarked,@Nullable,@NonNull) used instead.Objects.requireNonNull(...)— nullability expressed via JSpecify annotations.@NullMarked(org.jspecify.annotations.NullMarked).@NullMarked(org.jspecify.annotations.NullMarked).Optionalconsumed withifPresent/ifPresentOrElse/map/orElseThrow— neverorElse(unusedValue)nor anisPresent()+get()block.StringUtil.isBlank(...)used instead ofs == null || s.isBlank().Exceptions
catch (Exception e)— only specific exceptions are caught.throw new RuntimeException(...)/IllegalStateException(...)— these tear down the whole application.LOGGER.info("...", e)), not concatenated into the message string.Style and idioms
BibEntryobjects built with withers (withField, notsetField).List.of()/Map.of()/Set.of(),Path.of(),SequencedCollection/SequencedSet, text blocks.Pattern.compile(...)constant, notString.matches(...).org.jabref.logic.util.BackgroundTask, notnew Thread().///) uses Markdown syntax, not JavaDoc inline tags:`code`instead of{@code},[ClassName]instead of{@link}.User-facing text
Localization.langin Java,%prefix in FXML).!; labels do not end with:."...: %0"), not string concatenation.Security
text/htmlresponse — including exception/error messages, not just the success body (XSS).Tests
org.jabref.model/org.jabref.logichave added or updated tests.assertEquals), use plain JUnit asserts (not AssertJ), have no@DisplayName, do not catch exceptions (let them propagate so JUnit reports setup/teardown failures directly), and use@TempDirinstead of manual temp directories.2. Verification commands
./gradlew :jablib:check(or./gradlew checkfor all modules)../gradlew checkstyleMain checkstyleTest checkstyleJmh../gradlew modernizer../gradlew --no-configuration-cache :rewriteDryRunreports no changes (run./gradlew rewriteRunto fix)../gradlew javadoc.npx markdownlint-cli2 "docs/**/*.md" "*.md"(only if Markdown changed).npm ci && npm run textlintreports no misspellings (only if Markdown changed).rewriteRun:docker run -v $(pwd):/github/workspace ghcr.io/leventebajczi/intellij-format:master "*.java" "" ".idea/codeStyles/Project.xml".3. Documentation
CHANGELOG.mdentry added if the change is visible to the user (end-user wording, no extra blank lines, sorted in next to existing entries about the same component/feature). Link the issue if one exists; link the PR only when no issue exists. UseTODOas the placeholder when neither is known yet — never a fake number. No entry for fixes to changes that were themselves introduced after the last release (feature only in## [Unreleased]) — update the existing unreleased entry instead if needed.TODO(nocloses/fixesfor merely-similar issues).docs/requirements/<area>.mdif the change is a new feature or significant bug fix (skip for refactors, minor fixes, and internal changes).docs/updated if behavior or architecture changed.4. Pull request
.github/PULL_REQUEST_TEMPLATE.md, every section filled.[x],[ ], or[/].gh pr create --body-file <file>(not--body).CHANGELOG.mdused aTODOplaceholder (no issue confidently identified yet — an existing issue link always stays), the PR was opened as draft, the placeholder was replaced with the real PR-number link after PR creation, committed and pushed, and only then was the PR marked ready for review. If an issue is identified or created later, the link is switched to the issue.Checklist
CHANGELOG.mddescribing the change from the user's point of view (if the change is visible to the user)