Skip to content

fix: keep mirror credentials when saving repository settings - #47

Merged
TheMeinerLP merged 5 commits into
mainfrom
fix/keep-mirror-authorization
Oct 17, 2026
Merged

TheMeinerLP merged 5 commits into
mainfrom
fix/keep-mirror-authorization

Conversation

@TheMeinerLP

Copy link
Copy Markdown

Description

Saving the maven settings of a group could remove the loopback credentials of its members, so the group could no longer read its private members.

Staging evidence (image 23486856, main 19189c7). A dashboard save of repository onelitefeather with the type switched to GROUP. In the DB backup each loopback member (onelitefeather-releases, onelitefeather-proxy, onelitefeather-snapshots) had authorization: {method: LOOPBACK_LINK, login: reposilite-mirror, password: <secret>}. After the save the entries kept reference, store, allowedGroups, allowedExtensions, connectTimeout, readTimeout, httpProxy and authenticatedFetchingOnly, but had no authorization. The members releases and snapshots and the blank entry were gone as well. The blank entry is removed by a type switch, as before, and the confirmation now names it. The data alone does not show whether the user removed the other two members.

Root causes

  1. Frontend. The members picker rebuilt every mirrored entry from its id (EntryDetail.vue onMembers through proxiedOfMembers in groupMembers.js). Adding, moving or removing a member therefore wrote entries that held only the reference. Removing one id also removed every entry with that id.
  2. Backend. SettingsSecrets.restore skipped a field that the body left out (?: continue), so an absent authorization was stored as null. With hideSettingsSecrets off, SettingsEndpoints bound the body directly and merged nothing.

Changes

  • The picker works on the mirrored entries. Members that stay are the same objects, a new member is a bare reference, and a removal takes out only the place clicked.
  • The type switch lists the credentials that a switch removes on a line of their own, before it is confirmed.
  • A matched list entry keeps each secret that its body leaves out, with hideSettingsSecrets on and off. An explicit null removes a credential. When several stored entries share a reference, a secret is refused rather than given to the wrong entry.
  • Each group member shows its loopback login (never the password) and opens its credential for editing: login and password. A member without one gets a loopback credential when a login is typed.
  • docs/gitbook describes the kept secrets and the null removal.

Type of change

  • fix - bug fix
  • feat - the credential editor for loopback members
  • docs - settings reference

How was this tested?

Backend (targeted classes, two workers):

  • ./gradlew :reposilite-backend:test --tests 'com.reposilite.configuration.shared.SettingsSecretsTest': 21 tests, including the new rules for absent credentials, explicit null, absent lists, shared references and placeholders off.
  • ./gradlew :reposilite-backend:integration --tests 'com.reposilite.configuration.MirrorCredentials*' --tests 'com.reposilite.configuration.HiddenSecretsSettingsIntegrationTest' --tests 'com.reposilite.configuration.LocalSettingsIntegrationTest': the new MirrorCredentialsRoundTripSpecification runs with hidden and shown secrets (7 tests each: byte-for-byte round trip of the credentials, untyped group switched to GROUP, member sent back without its credential, member removed, explicit null, duplicate reference refused). Before the fix the absent-credential and duplicate tests failed in both modes.

Frontend (touched vitest files, two workers): groupMembers, RepositoryMembersPicker, EntryDetail.type, RepositoryWizard, repositoryWizard, repositoryTypes: 241 tests pass. ESLint is clean on the touched files.

Not run: the full backend and frontend suites, and ./gradlew build.

Checklist

  • Commit messages follow the Conventional Commits convention documented in CLAUDE.md
  • Tests cover the change
  • ./gradlew build passes locally (not run, see above)
  • Documentation updated for the user-facing behaviour (docs/gitbook/reference)
  • Existing copyright headers are preserved; the new test file carries the header
  • No credentials, tokens or personal data are included in the diff (the test fixtures use invented secrets)

The members picker of a group rebuilt every mirrored entry from its id, so
adding, moving or removing a member wrote back entries that held only the
reference. The server then stored the defaults in place of the credential,
store, allowed groups and timeouts of each member. Staging lost the loopback
credentials of a group this way.

The picker now works on the entries themselves. A member that stays is saved
as the same object, a new member gets a bare reference, and a removal takes
out only the place that was clicked, so two entries naming one repository are
told apart.
Switching a proxy to a group, or a group to a proxy, removes its mirrored
entries. The confirmation named them only by count, so the credentials they
carry went with no line of their own. The confirmation now lists those
credentials separately, before the switch is confirmed.
A write of the maven domain stored a repository's mirror without its
authorization whenever the body left that object out. Restoring a masked
placeholder only ever replaced a value that was sent, so an absent credential
was stored as null. The dashboard rebuilt group members from their ids, so
saving a group erased the loopback credentials of its members.

A matched list entry now keeps each secret its body leaves out, both with
hideSettingsSecrets on and off. An explicit null still removes a credential.
Entries are matched by their key and, when several stored entries share a
key, a secret that would have to come from one of them is refused rather
than given to the wrong entry. A placeholder is only replaced while secrets
are hidden, as before.
A group reads its members through their loopback credentials, but the group
editor showed only the member ids, so a credential could only be set by
editing the raw settings. Each member of a group now shows its login, never
its password, and opens its credential for editing: the login and the
password of the loopback link. A member without a credential gets a loopback
one when a login is typed. The other keys of the credential and of the member
are kept.
…ntials

A write that leaves a secret out of an existing list entry now keeps the
stored value, with hideSettingsSecrets on or off. A null authorization is the
way to remove a mirror credential. The settings reference said nothing about
either, so a client could not know that an omitted credential survives.
@github-actions

Copy link
Copy Markdown

Test results

  266 files    266 suites   58m 27s ⏱️
2 373 tests 2 372 ✅ 1 💤 0 ❌
2 439 runs  2 438 ✅ 1 💤 0 ❌

Results for commit dbf58fa.

@TheMeinerLP
TheMeinerLP merged commit 0b92ee6 into main Oct 17, 2026
10 checks passed
@TheMeinerLP
TheMeinerLP deleted the fix/keep-mirror-authorization branch October 17, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant