fix: restrict agnet simulation controls
This commit is contained in:
@@ -1230,6 +1230,10 @@ func normalizeAgnetSimulationEvents(values []string) []string {
|
||||
}
|
||||
|
||||
func AgnetSimulateUserDeploymentEvents(c *gin.Context) {
|
||||
if c.GetInt("role") < common.RoleAdminUser && !common.GetEnvOrDefaultBool("AGNET_SIMULATION_ENABLED", false) {
|
||||
agnetError(c, "SIMULATION_DISABLED", "simulation endpoint is admin-only unless AGNET_SIMULATION_ENABLED=true")
|
||||
return
|
||||
}
|
||||
record, ok := requireAuthenticatedUserAgnetDeployment(c)
|
||||
if !ok {
|
||||
return
|
||||
|
||||
@@ -504,6 +504,7 @@ func TestAgnetUserDeploymentSimulatedEventsArePersistedAndPrefixed(t *testing.T)
|
||||
simRecorder := httptest.NewRecorder()
|
||||
simCtx, _ := gin.CreateTestContext(simRecorder)
|
||||
simCtx.Set("id", 7)
|
||||
simCtx.Set("role", common.RoleAdminUser)
|
||||
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
|
||||
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{"events":["deployment.started","simulation.agent.done"]}`))
|
||||
simCtx.Request.Header.Set("Content-Type", "application/json")
|
||||
@@ -546,6 +547,7 @@ func TestAgnetUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T)
|
||||
simRecorder := httptest.NewRecorder()
|
||||
simCtx, _ := gin.CreateTestContext(simRecorder)
|
||||
simCtx.Set("id", 8)
|
||||
simCtx.Set("role", common.RoleAdminUser)
|
||||
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
|
||||
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`))
|
||||
simCtx.Request.Header.Set("Content-Type", "application/json")
|
||||
@@ -557,6 +559,37 @@ func TestAgnetUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T)
|
||||
require.Equal(t, "DEPLOYMENT_FORBIDDEN", envelope.Error.Code)
|
||||
}
|
||||
|
||||
func TestAgnetUserDeploymentSimulationIsDisabledForRegularUserByDefault(t *testing.T) {
|
||||
setupAgnetControlPlaneTestDB(t)
|
||||
resetAgnetControlPlaneState(t)
|
||||
|
||||
plan := baseAgnetResourceGrantPlan()
|
||||
plan.UserContext.UserID = "7"
|
||||
for idx := range plan.Agents[0].ResourceGrants {
|
||||
plan.Agents[0].ResourceGrants[idx].UserID = "7"
|
||||
}
|
||||
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
|
||||
require.Equal(t, http.StatusOK, createRecorder.Code)
|
||||
require.True(t, createEnvelope.Success)
|
||||
var createBody map[string]any
|
||||
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
|
||||
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
|
||||
|
||||
simRecorder := httptest.NewRecorder()
|
||||
simCtx, _ := gin.CreateTestContext(simRecorder)
|
||||
simCtx.Set("id", 7)
|
||||
simCtx.Set("role", common.RoleCommonUser)
|
||||
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
|
||||
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`))
|
||||
simCtx.Request.Header.Set("Content-Type", "application/json")
|
||||
AgnetSimulateUserDeploymentEvents(simCtx)
|
||||
|
||||
var envelope agnetCreateTestEnvelope
|
||||
require.NoError(t, common.Unmarshal(simRecorder.Body.Bytes(), &envelope))
|
||||
require.False(t, envelope.Success)
|
||||
require.Equal(t, "SIMULATION_DISABLED", envelope.Error.Code)
|
||||
}
|
||||
|
||||
func TestAgnetUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) {
|
||||
setupAgnetControlPlaneTestDB(t)
|
||||
resetAgnetControlPlaneState(t)
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { toast } from 'sonner'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
import { api } from '@/lib/api'
|
||||
// /audit pulls from mcp-server §5.10 stub now, not the Heicode-local
|
||||
// controller — the contract doc names that endpoint as the canonical
|
||||
@@ -349,6 +350,8 @@ function grantStatusToneClass(status: string | undefined): string {
|
||||
function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const currentUser = useAuthStore((state) => state.auth.user)
|
||||
const canSimulate = Boolean(currentUser?.role && currentUser.role >= 10)
|
||||
const phase = dep.phase || dep.status
|
||||
const risk = describeRiskLevel(dep)
|
||||
const grants = collectResourceGrants(dep)
|
||||
@@ -389,6 +392,7 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex shrink-0 items-center gap-2'>
|
||||
{canSimulate && (
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
@@ -400,6 +404,7 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
|
||||
<Rocket className='h-3.5 w-3.5' />
|
||||
{t('Simulate')}
|
||||
</Button>
|
||||
)}
|
||||
<StatusBadge phase={phase} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user