fix(installer): gate agent installer command behind INSTALL_AGENT capability - #7985
Hedi (heditar) wants to merge 20 commits into
Conversation
…ability (OpenAEV-Platform/filigran-private#320) - Add ResourceType.AGENT_INSTALLER and Capability.INSTALL_AGENT (group TARGETS, scope TENANT) so the agent installer command is no longer reachable without a dedicated capability. - Require INSTALL_AGENT on ExecutorApi#getOpenAevAgentInstaller instead of skipping RBAC, since that endpoint returns an install command embedding a service-account bearer token. - Hide the "Install simulation agents" TopBar button unless the user holds INSTALL_AGENT. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
📖 Documentation check — ❌ Failed 18 functional file(s) changed, 0 documentation file(s) changed. Blocking gapsUpdate the docs or add the 🟠 1 new endpoint(s) in existing controller
|
…penAEV-Platform/filigran-private#320) Display it in the capabilities screen under Security rather than Targets, since it gates a security-sensitive endpoint. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #7985 +/- ##
============================================
- Coverage 46.80% 46.16% -0.64%
- Complexity 12404 12406 +2
============================================
Files 3039 3015 -24
Lines 101275 100972 -303
Branches 16390 16264 -126
============================================
- Hits 47401 46616 -785
- Misses 50066 50629 +563
+ Partials 3808 3727 -81
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…enAEV-Platform/filigran-private#320) Capability labels shown in the capabilities screen are looked up by their raw enum value, so add "INSTALL_AGENT" to every locale. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… capability (#320) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
#320) Gates the raw executable and Windows package download endpoints (getOpenAevAgentExecutable, getOpenAevAgentPackage) behind the AGENT_RUNTIME_ACCESS capability, which now also grants (AGENT, READ). Previously these endpoints skipped RBAC entirely. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… downloads (#320) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…vice role capability (#320) - replace V20260915_Service_Account_Install_Agent datapack with a Flyway migration that backfills INSTALL_AGENT on the existing 'Service integration' role, matching the pattern used by other role-capability migrations - update ExecutorApiTest to require INSTALL_AGENT (not AGENT_RUNTIME_ACCESS) for the executable/package download endpoints, matching the AGENT_INSTALLER resource type they now check Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- mvn spotless:apply on ExecutorApi.java/ExecutorApiTest.java - regenerate api-types.d.ts to include INSTALL_AGENT capability and AGENT_INSTALLER resource type enum values Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| }, | ||
| produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) | ||
| @AccessControl(skipRBAC = true) | ||
| @AccessControl(actionPerformed = Action.READ, resourceType = ResourceType.AGENT_INSTALLER) |
There was a problem hiding this comment.
Since we are closing the agent binary endpoints, the implant binary should probably follow.
/api/implant/openaev/{platform}/{architecture} in InjectorApi is still permitAll, with accesscontrol(skipRBAC = true) and a // Public API comment. It is the same kind of endpoint, so leaving one protected and the other open is hard to justify.
Either we close it in the same wave, or we document why the two differ.
There was a problem hiding this comment.
yes I can do that, If it is ok for you I will do it in a follow up PR to keep the scope of this one small
…ALL_AGENT Constants.SERVICE_ROLE_CAPABILITIES now includes INSTALL_AGENT (backfilled onto the "Service integration" role by the Flyway migration), but this test still asserted the old two-capability set, failing CI's API Tests shard. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…released ones git describe on the PR's merge commit resolves to release 3.260917.1, which already ships V6_20260916173300000 (inject authorisation) and V6_20260916200000000 (agent status) — both dated after this migration's old 20260916150000000 timestamp. The Migrations Guard correctly flags that as an insert in the middle of the already-released block. Renaming to 20260917200000000 puts it after everything currently released. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit renamed the file but the follow-up `git add` silently no-opped (invalid old-path pathspec aborted the whole command before the new path was staged), so the committed content still had the class declared as V6_20260916150000000__... under the new filename — a filename/public-class-name mismatch that doesn't compile. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ExecutorApiTest.givenUserWithInstallAgentCapability_shouldReturnInstallerCommand calls GET /api/agent/installer/openaev/linux/service, which resolves to agents/openaev-agent/linux/openaev-agent-installer-Testing.sh under the "local" test profile. Windows had installer fixtures (session/service-user variants) but linux only had the upgrade-script and raw-binary fixtures — the installer one was never added, so the lookup threw "Agent installer version Testing not found". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…mmand
The install command's <pre> block rendered as soon as installerToken's
useEffect fired, but that fetch is async and the state starts at ''. The
generated command (curl ... -H "Authorization: Bearer ${installerToken}")
therefore briefly carried an empty bearer token, which the E2E test's
"not empty" check doesn't catch since the string itself is non-empty text.
Against the capability-gated installer endpoint, that empty-token command
fails auth silently, so the agent never installs — this is what was timing
out the infra-agent and infra-multitenant E2E specs (agent never registers
an endpoint within the 150s poll).
Show a spinner instead of the command until the token has actually loaded.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new installer-token flow needs robust failure handling (correct 404 behavior/no-store caching + frontend error state) and the Flyway migration naming should be aligned with repository conventions.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR introduces a new AGENT_INSTALLER resource type and an INSTALL_AGENT capability to restrict access to OpenAEV agent installation assets/commands, updates the frontend to hide the “Install agents” entry point unless the user holds that capability, and backfills the service role so service-account flows continue to work after the gating.
Changes:
- Add
ResourceType.AGENT_INSTALLERandCapability.INSTALL_AGENT, and include it in the per-tenant “Service integration” role. - Gate agent installer-related endpoints with
@AccessControl(... ResourceType.AGENT_INSTALLER)and add a new endpoint to retrieve the tenant service-account token for copy/paste installer commands. - Frontend: conditionally render the agents install entry point; fetch installer token and embed it in the generated install commands; update i18n and generated API types.
File summaries
| File | Description |
|---|---|
| openaev-model/src/main/java/io/openaev/database/model/ResourceType.java | Adds AGENT_INSTALLER resource type for RBAC checks. |
| openaev-model/src/main/java/io/openaev/database/model/Capability.java | Adds INSTALL_AGENT capability mapped to AGENT_INSTALLER read access. |
| openaev-front/src/utils/lang/en.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/fr.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/de.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/es.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/it.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/ja.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/ko.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/ru.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/lang/zh.json | Adds translation for INSTALL_AGENT. |
| openaev-front/src/utils/api-types.d.ts | Updates generated types to include AGENT_INSTALLER and INSTALL_AGENT. |
| openaev-front/src/admin/components/nav/TopBar.tsx | Hides the “Install simulation agents” link unless the user holds INSTALL_AGENT. |
| openaev-front/src/admin/components/agents/InstructionSelector.tsx | Fetches installer token and uses it in copy/paste install commands; adds loading UI. |
| openaev-front/src/actions/executors/executor-action.ts | Adds frontend action to fetch the installer token endpoint. |
| openaev-api/src/main/java/io/openaev/service/account/Constants.java | Adds INSTALL_AGENT to the service role capability set. |
| openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java | Gates installer/package/executable endpoints; adds new installer-token endpoint. |
| openaev-api/src/main/java/io/openaev/migration/V6_20260917200000000__Add_install_agent_capability_to_service_role.java | Migration to backfill INSTALL_AGENT into the “Service integration” role. |
| openaev-api/src/test/java/io/openaev/api/capabilities/CapabilityTreeBuilderTest.java | Updates expectations for service role capabilities. |
| openaev-api/src/test/java/io/openaev/rest/ExecutorApiTest.java | Updates/extends tests for new capability gating. |
| openaev-api/src/test/resources/agents/openaev-agent/linux/openaev-agent-installer-Testing.sh | Adds a test resource for installer script resolution. |
Review details
- Files reviewed: 20/21 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| useEffect(() => { | ||
| fetchOpenAevAgentInstallerToken(tenantPrefix).then(({ data }) => { | ||
| setInstallerToken(data); | ||
| }); | ||
| }, [tenantPrefix]); |
| @Component | ||
| public class V6_20260917200000000__Add_install_agent_capability_to_service_role | ||
| extends BaseJavaMigration { |
Laurent Giovannoni (laugiov)
left a comment
There was a problem hiding this comment.
A few things before I approve.
Copilot's blocking comments need addressing, except the migration naming one which is a false positive: every V6 migration here uses Sentence_case_with_underscores, yours is consistent.
On tests: everything new uses @WithMockUser, so nothing covers the anonymous call, which is what this PR closes. And the token endpoint has no test at all.
Last, AI told me that it clashes with #8009 on SERVICE_ROLE_CAPABILITIES, and both add a migration on roles_capabilities. End state should be AGENT_RUNTIME_ACCESS + AGENT_DOCUMENT_ACCESS + INSTALL_AGENT. Worth agreeing who merges first, a bad resolution drops a capability silently.
My apologies, I saw this comment right after merging #8009 :( If I need to rollbackn tell me but it would imply a rollback of the implant and oaev backend |
Added error handling for token retrieval and updated response headers. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
I merged your change already |
|
✅ Container vulnerability scan — Passed Previously reported findings are no longer present.
View workflow run · Standard JSON report · UBI9 JSON report Updated from CI run attempt 2. |
…lation A merge artifact left an extra closing parenthesis on SERVICE_ROLE_CAPABILITIES's initializer, breaking the build entirely. test(executor): verify agent endpoints reject fully unauthenticated requests None of the existing coverage exercised a request with zero authentication (only "authenticated but missing INSTALL_AGENT"). This is exactly the gap behind the infra-agent/infra-multitenant E2E failures: the currently released agent install script's internal executable-download curl sends no Authorization header at all, and gets a 401 from AccessControlAspect before the capability check even runs. Adds a dedicated test class (no @WithMockUser, so requests run as truly anonymous) asserting 401 on the executable, package, installer-command and installer-token endpoints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…token Drops the executable-download case (redundant with package/installer for regression-guard purposes) and documents, in the class Javadoc, why these stay in their own file: ExecutorApiTest carries a class-level @WithMockUser, and its custom listener authenticates the request through a channel that SecurityContextHolder.clearContext(), .with(anonymous()), and an explicit fresh MockHttpSession all failed to override (verified with debug instrumentation — the test thread's SecurityContextHolder was null on both sides of the call, yet the response still carried the mock user's identity). Omitting the annotation from the whole class hierarchy, as here, is the only approach that reliably reproduces zero authentication. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Proposed changes
PR for the agents: OpenAEV-Platform/agent#226
Testing Instructions
See with Hedi (@heditar)
Tested install and upgrade on mac
Related issues
Checklist