Skip to content

Allow admins to archive and unarchive any vault#430

Merged
tobihagemann merged 8 commits intodevelopfrom
feature/admin-archive-vault
Mar 27, 2026
Merged

Allow admins to archive and unarchive any vault#430
tobihagemann merged 8 commits intodevelopfrom
feature/admin-archive-vault

Conversation

@tobihagemann
Copy link
Copy Markdown
Member

@tobihagemann tobihagemann commented Mar 24, 2026

Fixes #283.

Admins can view all vaults but previously couldn't archive or unarchive vaults they don't own. The @VaultRole annotation's realmRole field was shared between the bypass check and the onMissingVault check, making it impossible to add admin bypass to the createOrUpdate endpoint without breaking vault creation permissions.

This PR introduces a bypassRealmRole field on the @VaultRole annotation to separate the two concerns. The bypass check is also moved into the vault-exists block so it only applies to existing vaults. Vault creation still requires the create-vaults role as before.

Instead of adding admin bypass to the general createOrUpdate endpoint (which would let admins modify vault name, description, and emergency key shares), a dedicated PUT /{vaultId}/archived endpoint handles archive toggling with its own admin bypass. The createOrUpdate endpoint remains restricted to vault owners.

On the frontend, archive and reactivate buttons are now visible to admin users who aren't vault owners. The buttons also appear in the vault recovery section, so admins who are vault owners but have lost their account key can still archive or reactivate. The setArchived response normalizes creationTime to a Date object so the vault detail view renders correctly after toggling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 24, 2026

Walkthrough

Removed the explicit realmRole = RealmRole.ADMIN requirement from GET /vaults/{vaultId}'s @VaultRole. Added PUT /vaults/{vaultId}/archived to set a vault's archived flag, persist changes, emit a vault-updated event, and return the updated VaultDto. VaultRole gained a new bypassRealmRole() attribute and Javadoc clarifications. VaultRoleFilter now evaluates realm-role and emergency-council bypasses only after confirming the vault exists and adjusted forbidden messaging. Frontend: added isAdmin prop, passed it from VaultList, added VaultService.setArchived, and updated archive/reactivate dialogs. Tests and CHANGELOG updated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • SailReal
  • overheadhunter
  • mindmonk
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Allow admins to archive and unarchive any vault' directly and accurately summarizes the main change across the changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation, implementation approach, and frontend changes for allowing admins to archive/unarchive vaults.
Linked Issues check ✅ Passed The PR fully addresses issue #283 by implementing both options: allowing admins to archive/unarchive vaults via a dedicated endpoint with admin bypass, and making buttons visible to admins who aren't owners.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the objective of allowing admins to archive/unarchive vaults; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/admin-archive-vault

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tobihagemann tobihagemann requested a review from SailReal March 24, 2026 20:44
Copy link
Copy Markdown
Member

@SailReal SailReal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmm in the current implementation an user with the admin role can now edit each vault. I think especially because of

vault.setRequiredEmergencyKeyShares(vaultDto.requiredEmergencyKeyShares);
and
// does this request update emergency key shares?
if (!oldEmergencyKeyShares.containsAll(vaultDto.emergencyKeyShares.values())) {
var emergencyAccessCouncilMembers = String.join("\", \"", vaultDto.emergencyKeyShares.keySet());
var settings = """
{ "requiredEmergencyKeyShares": %d, "emergencyCouncilMemberIds": ["%s"] }
""".formatted(vaultDto.requiredEmergencyKeyShares, emergencyAccessCouncilMembers);
eventLogger.logEmergencyAccessSetup(vault.getId(), currentUser.getId(), settings, request.remoteAddress().hostAddress());
}
this starts to be problematic. Maybe it is time to add next to this huge createOrUpdate thing an "patch" method where we only set specific values etc?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/src/main/java/org/cryptomator/hub/api/VaultResource.java (1)

504-504: Make the admin bypass explicit in the annotation.

Both endpoints currently rely on @VaultRole.bypassRealmRole() defaulting to RealmRole.ADMIN. Since this is authorization-sensitive, spelling out bypassRealmRole = RealmRole.ADMIN here would make the intent obvious and avoid silently changing behavior if the annotation default is ever adjusted.

Also applies to: 518-518

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/org/cryptomator/hub/api/VaultResource.java` at line
504, The `@VaultRole` usage in VaultResource is relying on the annotation default
for bypassRealmRole; update the annotations that currently read `@VaultRole`(value
= {VaultAccess.Role.MEMBER, VaultAccess.Role.OWNER}, onMissingVault =
VaultRole.OnMissingVault.NOT_FOUND) to explicitly set bypassRealmRole =
RealmRole.ADMIN so the admin bypass is explicit; apply the same change to the
other `@VaultRole` instance mentioned (the second occurrence near the other
method) to ensure both annotations explicitly declare bypassRealmRole =
RealmRole.ADMIN.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/common/backend.ts`:
- Around line 363-367: setArchived currently returns the raw response body as a
VaultDto (string creationTime) which violates the contract and differs from
get() that converts creationTime to a Date; update setArchived (the public async
setArchived(vaultId: string, archived: boolean) method) to parse/normalize the
returned payload's creationTime into a Date before resolving (same normalization
used by get()), and keep the existing error handling via
rethrowAndConvertIfExpected so callers always receive a VaultDto with
creationTime as a Date.

---

Nitpick comments:
In `@backend/src/main/java/org/cryptomator/hub/api/VaultResource.java`:
- Line 504: The `@VaultRole` usage in VaultResource is relying on the annotation
default for bypassRealmRole; update the annotations that currently read
`@VaultRole`(value = {VaultAccess.Role.MEMBER, VaultAccess.Role.OWNER},
onMissingVault = VaultRole.OnMissingVault.NOT_FOUND) to explicitly set
bypassRealmRole = RealmRole.ADMIN so the admin bypass is explicit; apply the
same change to the other `@VaultRole` instance mentioned (the second occurrence
near the other method) to ensure both annotations explicitly declare
bypassRealmRole = RealmRole.ADMIN.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 03078181-cfc9-4a35-bea6-c7a60b5827e8

📥 Commits

Reviewing files that changed from the base of the PR and between 0686dcc and 790ecf6.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
  • backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java
  • frontend/src/common/backend.ts
  • frontend/src/components/ArchiveVaultDialog.vue
  • frontend/src/components/ReactivateVaultDialog.vue
  • frontend/src/components/VaultDetails.vue
✅ Files skipped from review due to trivial changes (2)
  • CHANGELOG.md
  • backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/VaultDetails.vue

Comment thread frontend/src/common/backend.ts
@tobihagemann tobihagemann requested a review from SailReal March 26, 2026 09:42
Copy link
Copy Markdown
Member

@SailReal SailReal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks quite good, but if the admin is also owner and has lost the account key, the admin should still be able to archive the vault:

Image

Comment thread backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
Comment thread backend/src/main/java/org/cryptomator/hub/api/VaultResource.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java (1)

965-1025: Add a non-existing vault test for PUT /{vaultId}/archived (expect 404).

The PR’s core auth change is “bypass only after vault lookup.” This should be locked with an explicit 404 test on a non-existing vault ID.

✅ Suggested test addition
+		`@Test`
+		`@Order`(5)
+		`@DisplayName`("PUT /vaults/7E57C0DE-0000-4000-8000-BADBADBADBAD/archived returns 404 for non-existing vault")
+		void testAdminArchiveNonExistingVault() {
+			given().contentType(ContentType.TEXT).body("true")
+					.when().put("/vaults/{vaultId}/archived", "7E57C0DE-0000-4000-8000-BADBADBADBAD")
+					.then().statusCode(404);
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java` around
lines 965 - 1025, Add a new test method inside the AdminArchiveVault nested
class that verifies PUT /vaults/{vaultId}/archived for a non-existent UUID
returns 404; name it e.g. testAdminArchiveNonExistingVault404 and use the same
request style as testAdminArchiveVault
(given().contentType(ContentType.TEXT).body("true").when().put("/vaults/{vaultId}/archived",
"<non-existing-UUID>").then().statusCode(404)); annotate similarly (e.g. `@Test`,
`@Order`, `@DisplayName`) and include `@DBRollbackAfter` if needed to match
surrounding tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java`:
- Around line 965-1025: Add a new test method inside the AdminArchiveVault
nested class that verifies PUT /vaults/{vaultId}/archived for a non-existent
UUID returns 404; name it e.g. testAdminArchiveNonExistingVault404 and use the
same request style as testAdminArchiveVault
(given().contentType(ContentType.TEXT).body("true").when().put("/vaults/{vaultId}/archived",
"<non-existing-UUID>").then().statusCode(404)); annotate similarly (e.g. `@Test`,
`@Order`, `@DisplayName`) and include `@DBRollbackAfter` if needed to match
surrounding tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 065bf560-2c4f-4c00-85e0-5d7f72df7297

📥 Commits

Reviewing files that changed from the base of the PR and between cd11e3d and fd9d939.

📒 Files selected for processing (3)
  • backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
  • backend/src/test/java/org/cryptomator/hub/api/VaultResourceIT.java
  • frontend/src/common/backend.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/common/backend.ts
  • backend/src/main/java/org/cryptomator/hub/api/VaultResource.java

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 27, 2026

It looks quite good, but if the admin is also owner and has lost the account key, the admin should still be able to archive the vault:

<img width="3140" height="886" alt="Image" src="https://github.c...

This was already fixed in commit a7c463f. The archive and reactivate buttons are now included in the vault recovery section (vaultRecoveryRequired block), guarded by canToggleArchive which is true for both owners and admins. So an admin who is also the vault owner and has lost their account key will see those buttons alongside "Recover Vault Access".

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh issue create --repo cryptomator/hub --title Check seat count when unarchiving a vault --body When unarchiving a vault, the available seat count should be checked to ensure the license is not exceeded. Currently, the setArchivedendpoint (PUT/{vaultId}/archived) does not verify whether unarchiving the vault would cause the seat count to excee (http block)
    • Triggering command: /usr/bin/gh gh issue list --repo cryptomator/hub --limit 3 (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@tobihagemann tobihagemann merged commit 445fb27 into develop Mar 27, 2026
10 checks passed
@tobihagemann tobihagemann deleted the feature/admin-archive-vault branch March 27, 2026 10:36
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.

Hide the 'Archive Vault' button for admin users without ownership rights, or allow admins to actually do this

3 participants