Replace third-party owners with administrators
Some checks failed
github / Analyze (go) (push) Has been cancelled
github / Analyze (actions) (push) Has been cancelled
github / Analyze (javascript-typescript) (push) Has been cancelled
make / build-apps (push) Has been cancelled
make / probod binary (darwin/amd64) (push) Has been cancelled
make / probod binary (freebsd/amd64) (push) Has been cancelled
make / probod binary (linux/amd64) (push) Has been cancelled
make / probod binary (openbsd/amd64) (push) Has been cancelled
make / probod binary (windows/amd64) (push) Has been cancelled
make / probod binary (darwin/arm64) (push) Has been cancelled
make / probod binary (freebsd/arm64) (push) Has been cancelled
make / probod binary (linux/arm64) (push) Has been cancelled
make / probod binary (openbsd/arm64) (push) Has been cancelled
make / probo-agent (darwin/amd64) (push) Has been cancelled
make / probo-agent (freebsd/amd64) (push) Has been cancelled
make / probo-agent (linux/amd64) (push) Has been cancelled
make / probo-agent (windows/amd64) (push) Has been cancelled
make / probo-agent (darwin/arm64) (push) Has been cancelled
make / probo-agent (freebsd/arm64) (push) Has been cancelled
make / probo-agent (linux/arm64) (push) Has been cancelled
make / probo-agent (windows/arm64) (push) Has been cancelled
make / docker (amd64) (push) Has been cancelled
make / docker (arm64) (push) Has been cancelled
make / snapshot-scan (push) Has been cancelled
make / build-probod (push) Has been cancelled
make / build-probo-agent (push) Has been cancelled
make / lint-go (push) Has been cancelled
make / lint-js (push) Has been cancelled
make / lint-swift (push) Has been cancelled
make / lint-shell (push) Has been cancelled
make / test (push) Has been cancelled
make / test-e2e (push) Has been cancelled
trufflehog / scan (push) Has been cancelled

Migrate business and security owners into a shared administrators list across GraphQL, MCP, CLI, n8n, and the console.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
Sacha Al Himdani
2026-07-27 12:24:43 +02:00
parent 084726dbad
commit 6a4f124adb
31 changed files with 808 additions and 601 deletions

View File

@@ -90,7 +90,17 @@ func (r *Resolver) ListThirdPartiesTool(ctx context.Context, req *mcp.CallToolRe
panic(fmt.Errorf("cannot list organization thirdParties: %w", err))
}
return nil, types.NewListThirdPartiesOutput(page), nil
thirdPartyIDs := make([]gid.GID, len(page.Data))
for i, tp := range page.Data {
thirdPartyIDs[i] = tp.ID
}
administratorIDsByThirdPartyID, err := prb.ThirdParties.MapAdministratorIDsForThirdPartyIDs(ctx, scope, thirdPartyIDs)
if err != nil {
return nil, types.ListThirdPartiesOutput{}, fmt.Errorf("cannot load third party administrators: %w", err)
}
return nil, types.NewListThirdPartiesOutput(page, administratorIDsByThirdPartyID), nil
}
// AddThirdPartyTool handles the addThirdParty tool
@@ -135,8 +145,7 @@ func (r *Resolver) AddThirdPartyTool(ctx context.Context, req *mcp.CallToolReque
SubprocessorsListURL: input.SubprocessorsListURL,
Certifications: input.Certifications,
Countries: countries,
BusinessOwnerID: input.BusinessOwnerID,
SecurityOwnerID: input.SecurityOwnerID,
AdministratorIDs: input.AdministratorIds,
StatusPageURL: input.StatusPageURL,
TermsOfServiceURL: input.TermsOfServiceURL,
SecurityPageURL: input.SecurityPageURL,
@@ -147,7 +156,7 @@ func (r *Resolver) AddThirdPartyTool(ctx context.Context, req *mcp.CallToolReque
return nil, types.AddThirdPartyOutput{}, fmt.Errorf("failed to create thirdParty: %w", err)
}
return nil, types.NewAddThirdPartyOutput(thirdParty), nil
return nil, types.NewAddThirdPartyOutput(thirdParty, input.AdministratorIds), nil
}
// UpdateThirdPartyTool handles the updateThirdParty tool
@@ -225,14 +234,9 @@ func (r *Resolver) UpdateThirdPartyTool(ctx context.Context, req *mcp.CallToolRe
trustPageURL = &input.TrustPageURL
}
var businessOwnerID **gid.GID
if input.BusinessOwnerID != nil {
businessOwnerID = &input.BusinessOwnerID
}
var securityOwnerID **gid.GID
if input.SecurityOwnerID != nil {
securityOwnerID = &input.SecurityOwnerID
var administratorIDs *[]gid.GID
if input.AdministratorIds != nil {
administratorIDs = &input.AdministratorIds
}
var category *coredata.ThirdPartyCategory
@@ -267,8 +271,7 @@ func (r *Resolver) UpdateThirdPartyTool(ctx context.Context, req *mcp.CallToolRe
SubprocessorsListURL: subprocessorsListURL,
Certifications: input.Certifications,
Countries: countries,
BusinessOwnerID: businessOwnerID,
SecurityOwnerID: securityOwnerID,
AdministratorIDs: administratorIDs,
StatusPageURL: statusPageURL,
TermsOfServiceURL: termsOfServiceURL,
SecurityPageURL: securityPageURL,
@@ -279,7 +282,12 @@ func (r *Resolver) UpdateThirdPartyTool(ctx context.Context, req *mcp.CallToolRe
return nil, types.UpdateThirdPartyOutput{}, fmt.Errorf("failed to update thirdParty: %w", err)
}
return nil, types.NewUpdateThirdPartyOutput(thirdParty), nil
administratorIDsByThirdPartyID, err := svc.ThirdParties.MapAdministratorIDsForThirdPartyIDs(ctx, scope, []gid.GID{thirdParty.ID})
if err != nil {
return nil, types.UpdateThirdPartyOutput{}, fmt.Errorf("cannot load third party administrators: %w", err)
}
return nil, types.NewUpdateThirdPartyOutput(thirdParty, administratorIDsByThirdPartyID[thirdParty.ID]), nil
}
func (r *Resolver) ListRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRisksInput) (*mcp.CallToolResult, types.ListRisksOutput, error) {
@@ -5414,8 +5422,13 @@ func (r *Resolver) VetThirdPartyTool(ctx context.Context, req *mcp.CallToolReque
return nil, types.VetThirdPartyOutput{}, fmt.Errorf("internal server error")
}
administratorIDsByThirdPartyID, err := r.proboSvc.ThirdParties.MapAdministratorIDsForThirdPartyIDs(ctx, scope, []gid.GID{thirdParty.ID})
if err != nil {
return nil, types.VetThirdPartyOutput{}, fmt.Errorf("cannot load third party administrators: %w", err)
}
return nil, types.VetThirdPartyOutput{
ThirdParty: types.NewThirdParty(thirdParty),
ThirdParty: types.NewThirdParty(thirdParty, administratorIDsByThirdPartyID[thirdParty.ID]),
}, nil
}
@@ -6318,7 +6331,17 @@ func (r *Resolver) ListChildThirdPartiesTool(ctx context.Context, req *mcp.CallT
panic(fmt.Errorf("cannot list child third parties: %w", err))
}
return nil, types.NewListChildThirdPartiesOutput(page), nil
thirdPartyIDs := make([]gid.GID, len(page.Data))
for i, tp := range page.Data {
thirdPartyIDs[i] = tp.ID
}
administratorIDsByThirdPartyID, err := r.proboSvc.ThirdParties.MapAdministratorIDsForThirdPartyIDs(ctx, scope, thirdPartyIDs)
if err != nil {
return nil, types.ListChildThirdPartiesOutput{}, fmt.Errorf("cannot load third party administrators: %w", err)
}
return nil, types.NewListChildThirdPartiesOutput(page, administratorIDsByThirdPartyID), nil
}
func (r *Resolver) ListRiskAssessmentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRiskAssessmentsInput) (*mcp.CallToolResult, types.ListRiskAssessmentsOutput, error) {
@@ -7658,8 +7681,13 @@ func (r *Resolver) GetThirdPartyTool(ctx context.Context, req *mcp.CallToolReque
return nil, types.GetThirdPartyOutput{}, fmt.Errorf("cannot get third party: %w", err)
}
administratorIDsByThirdPartyID, err := r.proboSvc.ThirdParties.MapAdministratorIDsForThirdPartyIDs(ctx, scope, []gid.GID{thirdParty.ID})
if err != nil {
return nil, types.GetThirdPartyOutput{}, fmt.Errorf("cannot load third party administrators: %w", err)
}
return nil, types.GetThirdPartyOutput{
ThirdParty: types.NewThirdParty(thirdParty),
ThirdParty: types.NewThirdParty(thirdParty, administratorIDsByThirdPartyID[thirdParty.ID]),
}, nil
}

View File

@@ -825,16 +825,11 @@ components:
items:
type: string
description: Countries or regions (ISO 3166-1 alpha-2 country codes, EU, or GLOBAL)
business_owner_id:
anyOf:
- $ref: "#/components/schemas/GID"
- type: "null"
description: Business owner ID
security_owner_id:
anyOf:
- $ref: "#/components/schemas/GID"
- type: "null"
description: Security owner ID
administrator_ids:
type: array
items:
$ref: "#/components/schemas/GID"
description: Administrator profile IDs
status_page_url:
type:
- string
@@ -1051,12 +1046,11 @@ components:
items:
type: string
description: Countries or regions (ISO 3166-1 alpha-2 country codes, EU, or GLOBAL)
business_owner_id:
$ref: "#/components/schemas/GID"
description: Business owner ID
security_owner_id:
$ref: "#/components/schemas/GID"
description: Security owner ID
administrator_ids:
type: array
items:
$ref: "#/components/schemas/GID"
description: Administrator profile IDs
status_page_url:
type: string
description: Status page URL
@@ -1152,12 +1146,11 @@ components:
items:
type: string
description: Countries or regions (ISO 3166-1 alpha-2 country codes, EU, or GLOBAL)
business_owner_id:
$ref: "#/components/schemas/GID"
description: Business owner ID
security_owner_id:
$ref: "#/components/schemas/GID"
description: Security owner ID
administrator_ids:
type: array
items:
$ref: "#/components/schemas/GID"
description: Administrator profile IDs
status_page_url:
type: string
description: Status page URL

View File

@@ -22,6 +22,7 @@ package types
import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
@@ -64,12 +65,16 @@ func NewAddThirdPartyRiskAssessmentOutput(v *coredata.ThirdPartyRiskAssessment)
}
}
func NewThirdParty(v *coredata.ThirdParty) *ThirdParty {
func NewThirdParty(v *coredata.ThirdParty, administratorIDs []gid.GID) *ThirdParty {
countries := make([]string, len(v.Countries))
for i, c := range v.Countries {
countries[i] = string(c)
}
if administratorIDs == nil {
administratorIDs = []gid.GID{}
}
return &ThirdParty{
ID: v.ID,
OrganizationID: v.OrganizationID,
@@ -86,8 +91,7 @@ func NewThirdParty(v *coredata.ThirdParty) *ThirdParty {
SubprocessorsListURL: v.SubprocessorsListURL,
Certifications: v.Certifications,
Countries: countries,
BusinessOwnerID: v.BusinessOwnerID,
SecurityOwnerID: v.SecurityOwnerID,
AdministratorIds: administratorIDs,
StatusPageURL: v.StatusPageURL,
TermsOfServiceURL: v.TermsOfServiceURL,
SecurityPageURL: v.SecurityPageURL,
@@ -98,10 +102,13 @@ func NewThirdParty(v *coredata.ThirdParty) *ThirdParty {
}
}
func NewListThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField]) ListThirdPartiesOutput {
func NewListThirdPartiesOutput(
thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField],
administratorIDsByThirdPartyID map[gid.GID][]gid.GID,
) ListThirdPartiesOutput {
thirdParties := make([]*ThirdParty, 0, len(thirdPartyPage.Data))
for _, v := range thirdPartyPage.Data {
thirdParties = append(thirdParties, NewThirdParty(v))
thirdParties = append(thirdParties, NewThirdParty(v, administratorIDsByThirdPartyID[v.ID]))
}
var nextCursor *page.CursorKey
@@ -117,10 +124,13 @@ func NewListThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdParty, c
}
}
func NewListChildThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField]) ListChildThirdPartiesOutput {
func NewListChildThirdPartiesOutput(
thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField],
administratorIDsByThirdPartyID map[gid.GID][]gid.GID,
) ListChildThirdPartiesOutput {
thirdParties := make([]*ThirdParty, 0, len(thirdPartyPage.Data))
for _, v := range thirdPartyPage.Data {
thirdParties = append(thirdParties, NewThirdParty(v))
thirdParties = append(thirdParties, NewThirdParty(v, administratorIDsByThirdPartyID[v.ID]))
}
var nextCursor *page.CursorKey
@@ -136,15 +146,15 @@ func NewListChildThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdPar
}
}
func NewAddThirdPartyOutput(v *coredata.ThirdParty) AddThirdPartyOutput {
func NewAddThirdPartyOutput(v *coredata.ThirdParty, administratorIDs []gid.GID) AddThirdPartyOutput {
return AddThirdPartyOutput{
ThirdParty: NewThirdParty(v),
ThirdParty: NewThirdParty(v, administratorIDs),
}
}
func NewUpdateThirdPartyOutput(v *coredata.ThirdParty) UpdateThirdPartyOutput {
func NewUpdateThirdPartyOutput(v *coredata.ThirdParty, administratorIDs []gid.GID) UpdateThirdPartyOutput {
return UpdateThirdPartyOutput{
ThirdParty: NewThirdParty(v),
ThirdParty: NewThirdParty(v, administratorIDs),
}
}