From 98a8d9039163174ba1bc285fc3c2d767be1b71aa Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Mon, 8 Jun 2026 14:50:23 +0200 Subject: [PATCH] Relocate agent-run authorization to its package The agent-run actions and policies lived in the core probo policy set, which forced every authorization change for the agent-run domain to touch unrelated core files. Move the actions and the OWNER/ADMIN and VIEWER/AUDITOR policies into the agentrun package and have it expose a PolicySet that probod registers into the authorizer at composition time, so the rules live alongside the domain logic they govern. Signed-off-by: Bryan Frimin --- pkg/agentrun/actions.go | 23 ++++++++ pkg/agentrun/policies.go | 56 +++++++++++++++++++ pkg/probo/actions.go | 4 -- pkg/probo/policies.go | 2 - pkg/probod/probod.go | 1 + pkg/server/api/console/v1/base_resolvers.go | 3 +- .../api/console/v1/organization_resolvers.go | 3 +- 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 pkg/agentrun/actions.go create mode 100644 pkg/agentrun/policies.go diff --git a/pkg/agentrun/actions.go b/pkg/agentrun/actions.go new file mode 100644 index 000000000..e93570851 --- /dev/null +++ b/pkg/agentrun/actions.go @@ -0,0 +1,23 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package agentrun + +// Agent run service actions. +// Format: agent:run: +const ( + ActionAgentRunGet = "agent:run:get" + ActionAgentRunList = "agent:run:list" + ActionAgentRunApprove = "agent:run:approve" +) diff --git a/pkg/agentrun/policies.go b/pkg/agentrun/policies.go new file mode 100644 index 000000000..75fd61e39 --- /dev/null +++ b/pkg/agentrun/policies.go @@ -0,0 +1,56 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package agentrun + +import ( + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/iam/policy" +) + +var organizationCondition = policy.Equals("principal.organization_id", "resource.organization_id") + +// FullAccessPolicy grants complete agent-run access, including approval +// decisions, to organization owners and admins. +var FullAccessPolicy = policy.NewPolicy( + "agentrun:full-access", + "Agent Run Full Access", + policy.Allow( + ActionAgentRunGet, + ActionAgentRunList, + ActionAgentRunApprove, + ).WithSID("agent-run-full-access").When(organizationCondition), +).WithDescription("Full agent-run access including approval decisions") + +// ReadAccessPolicy grants read-only agent-run access to viewers and auditors. +var ReadAccessPolicy = policy.NewPolicy( + "agentrun:read-access", + "Agent Run Read Access", + policy.Allow( + ActionAgentRunGet, + ActionAgentRunList, + ).WithSID("agent-run-read-access").When(organizationCondition), +).WithDescription("Read-only agent-run access") + +// PolicySet returns the PolicySet for the agent-run service. It is owned by +// this package and registered into the authorizer at composition time so the +// agent-run authorization rules live alongside the agent-run domain logic +// instead of in the core probo policy set. +func PolicySet() *iam.PolicySet { + return iam.NewPolicySet(). + AddRolePolicy("OWNER", FullAccessPolicy). + AddRolePolicy("ADMIN", FullAccessPolicy). + AddRolePolicy("VIEWER", ReadAccessPolicy). + AddRolePolicy("AUDITOR", ReadAccessPolicy) +} diff --git a/pkg/probo/actions.go b/pkg/probo/actions.go index b46231777..e5530151b 100644 --- a/pkg/probo/actions.go +++ b/pkg/probo/actions.go @@ -133,10 +133,6 @@ const ( ActionThirdPartyRiskAssessmentCreate = "core:thirdParty-risk-assessment:create" ActionThirdPartyRiskAssessmentList = "core:thirdParty-risk-assessment:list" - // AgentRun actions - ActionAgentRunGet = "core:agent-run:get" - ActionAgentRunList = "core:agent-run:list" - // Framework actions ActionFrameworkGet = "core:framework:get" ActionFrameworkList = "core:framework:list" diff --git a/pkg/probo/policies.go b/pkg/probo/policies.go index 4842a0335..e81cdc390 100644 --- a/pkg/probo/policies.go +++ b/pkg/probo/policies.go @@ -56,7 +56,6 @@ var ViewerPolicy = policy.NewPolicy( ActionThirdPartyDataPrivacyAgreementGet, ActionThirdPartyRiskAssessmentList, ActionThirdPartyRelationList, - ActionAgentRunGet, ActionAgentRunList, ActionFrameworkGet, ActionFrameworkList, ActionControlGet, ActionControlList, ActionMeasureGet, ActionMeasureList, @@ -145,7 +144,6 @@ var AuditorPolicy = policy.NewPolicy( ActionThirdPartyDataPrivacyAgreementGet, ActionThirdPartyRiskAssessmentList, ActionThirdPartyRelationList, - ActionAgentRunGet, ActionAgentRunList, ActionFrameworkGet, ActionFrameworkList, ActionControlGet, ActionControlList, ActionMeasureGet, ActionMeasureList, diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index 58746c0bc..f29e9e301 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -595,6 +595,7 @@ func (impl *Implm) Run( ) agentRunService := agentrun.NewService(pgClient) + iamService.Authorizer.RegisterPolicySet(agentrun.PolicySet()) thirdPartyService := thirdparty.NewService(pgClient, fileService, thirdPartyVetter) riskManagementService := riskmanagement.NewService(pgClient) diff --git a/pkg/server/api/console/v1/base_resolvers.go b/pkg/server/api/console/v1/base_resolvers.go index f3cd8f19a..0ebeed6c4 100644 --- a/pkg/server/api/console/v1/base_resolvers.go +++ b/pkg/server/api/console/v1/base_resolvers.go @@ -11,6 +11,7 @@ import ( "fmt" "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" @@ -370,7 +371,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error return types.NewWebhookSubscription(wc), nil } case coredata.AgentRunEntityType: - action = probo.ActionAgentRunGet + action = agentrun.ActionAgentRunGet loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) { run, err := r.agentRun.Get(ctx, scope, id) if err != nil { diff --git a/pkg/server/api/console/v1/organization_resolvers.go b/pkg/server/api/console/v1/organization_resolvers.go index f411cb936..d23f62cf6 100644 --- a/pkg/server/api/console/v1/organization_resolvers.go +++ b/pkg/server/api/console/v1/organization_resolvers.go @@ -12,6 +12,7 @@ import ( "time" "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" @@ -1175,7 +1176,7 @@ func (r *organizationResolver) Tasks(ctx context.Context, obj *types.Organizatio // AgentRuns is the resolver for the agentRuns field. func (r *organizationResolver) AgentRuns(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AgentRunOrderBy) (*types.AgentRunConnection, error) { - scope, err := r.authorize(ctx, obj.ID, probo.ActionAgentRunList) + scope, err := r.authorize(ctx, obj.ID, agentrun.ActionAgentRunList) if err != nil { return nil, err }