Introduce access-review source snapshot and normalize naming

Decouple each campaign from the live access-review sources it was started
with by introducing a per-campaign source snapshot table
(access_review_campaign_sources). The snapshot captures the source name,
category, and connector at start time, so a review remains coherent even
after the underlying source is edited or deleted. Fetch tracking becomes
an append-only log (access_review_campaign_source_fetch_attempts) that
preserves every attempt with its own status and error rather than
overwriting a single row.

Rename the shared access-review tables and enums to use a consistent
access_review_ prefix throughout:

  access_entries          → access_review_entries
  access_sources          → access_review_sources
  access_source_category  → access_review_source_category
  access_entry_*          → access_review_entry_*

The same rename propagates to every coredata type, service, GraphQL
schema, MCP specification, CLI command, frontend component, and e2e test.
The accessreview package gains dedicated actions.go and policies.go files
for its own IAM policy set, mirroring the agentrun package pattern.

Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
Bryan Frimin
2026-06-11 15:58:30 +02:00
parent 8a53e0ec1c
commit 4b64e59da4
146 changed files with 4795 additions and 4069 deletions

View File

@@ -3382,7 +3382,7 @@ func (r *Resolver) ListFindingAuditsTool(ctx context.Context, req *mcp.CallToolR
// ListAccessReviewCampaignsTool handles the listAccessReviewCampaigns tool
// List access review campaigns for an organization
func (r *Resolver) ListAccessReviewCampaignsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListAccessReviewCampaignsInput) (*mcp.CallToolResult, types.ListAccessReviewCampaignsOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, probo.ActionAccessReviewCampaignList)
scope, err := r.Authorize(ctx, input.OrganizationID, accessreview.ActionCampaignList)
if err != nil {
return nil, types.ListAccessReviewCampaignsOutput{}, err
}
@@ -3401,7 +3401,7 @@ func (r *Resolver) ListAccessReviewCampaignsTool(ctx context.Context, req *mcp.C
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
p, err := r.accessReview.Campaigns(scope).ListForOrganizationID(ctx, input.OrganizationID, cursor)
p, err := r.accessReview.ListCampaignsForOrganizationID(ctx, scope, input.OrganizationID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list access review campaigns: %w", err))
}
@@ -3412,18 +3412,18 @@ func (r *Resolver) ListAccessReviewCampaignsTool(ctx context.Context, req *mcp.C
// ListAccessEntriesTool handles the listAccessEntries tool
// List access entries for a campaign with optional filters
func (r *Resolver) ListAccessEntriesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListAccessEntriesInput) (*mcp.CallToolResult, types.ListAccessEntriesOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessEntryList)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionEntryList)
if err != nil {
return nil, types.ListAccessEntriesOutput{}, err
}
pageOrderBy := page.OrderBy[coredata.AccessEntryOrderField]{
Field: coredata.AccessEntryOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.AccessReviewEntryOrderField]{
Field: coredata.AccessReviewEntryOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if input.OrderBy != nil {
pageOrderBy = page.OrderBy[coredata.AccessEntryOrderField]{
pageOrderBy = page.OrderBy[coredata.AccessReviewEntryOrderField]{
Field: input.OrderBy.Field,
Direction: input.OrderBy.Direction,
}
@@ -3431,9 +3431,9 @@ func (r *Resolver) ListAccessEntriesTool(ctx context.Context, req *mcp.CallToolR
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
var filter *coredata.AccessEntryFilter
var filter *coredata.AccessReviewEntryFilter
if input.Filter != nil {
filter = &coredata.AccessEntryFilter{
filter = &coredata.AccessReviewEntryFilter{
Decision: input.Filter.Decision,
Flag: input.Filter.Flag,
IncrementalTag: input.Filter.IncrementalTag,
@@ -3444,15 +3444,16 @@ func (r *Resolver) ListAccessEntriesTool(ctx context.Context, req *mcp.CallToolR
}
}
var p *page.Page[*coredata.AccessEntry, coredata.AccessEntryOrderField]
var p *page.Page[*coredata.AccessReviewEntry, coredata.AccessReviewEntryOrderField]
if input.AccessSourceID != nil {
if input.AccessReviewCampaignSourceID != nil {
var err error
p, err = r.accessReview.Entries(scope).ListForCampaignIDAndSourceID(
p, err = r.accessReview.ListEntriesForCampaignIDAndSourceID(
ctx,
scope,
input.CampaignID,
*input.AccessSourceID,
*input.AccessReviewCampaignSourceID,
cursor,
filter,
)
@@ -3462,7 +3463,7 @@ func (r *Resolver) ListAccessEntriesTool(ctx context.Context, req *mcp.CallToolR
} else {
var err error
p, err = r.accessReview.Entries(scope).ListForCampaignID(ctx, input.CampaignID, cursor, filter)
p, err = r.accessReview.ListEntriesForCampaignID(ctx, scope, input.CampaignID, cursor, filter)
if err != nil {
panic(fmt.Errorf("cannot list access entries: %w", err))
}
@@ -3471,44 +3472,44 @@ func (r *Resolver) ListAccessEntriesTool(ctx context.Context, req *mcp.CallToolR
return nil, types.NewListAccessEntriesOutput(p), nil
}
// GetAccessReviewCampaignStatisticsTool handles the getAccessReviewCampaignStatistics tool
// GetAccessReviewStatisticsTool handles the getAccessReviewCampaignStatistics tool
// Get statistics for an access review campaign
func (r *Resolver) GetAccessReviewCampaignStatisticsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetAccessReviewCampaignStatisticsInput) (*mcp.CallToolResult, types.GetAccessReviewCampaignStatisticsOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignGet)
func (r *Resolver) GetAccessReviewStatisticsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetAccessReviewStatisticsInput) (*mcp.CallToolResult, types.GetAccessReviewStatisticsOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignGet)
if err != nil {
return nil, types.GetAccessReviewCampaignStatisticsOutput{}, err
return nil, types.GetAccessReviewStatisticsOutput{}, err
}
stats, err := r.accessReview.Entries(scope).Statistics(ctx, input.CampaignID)
stats, err := r.accessReview.CampaignStatistics(ctx, scope, input.CampaignID)
if err != nil {
panic(fmt.Errorf("cannot get campaign statistics: %w", err))
}
return nil, types.GetAccessReviewCampaignStatisticsOutput{
Statistics: types.NewAccessEntryStatistics(stats),
return nil, types.GetAccessReviewStatisticsOutput{
Statistics: types.NewAccessReviewStatistics(stats),
}, nil
}
// RecordAccessEntryDecisionTool handles the recordAccessEntryDecision tool
// RecordAccessReviewEntryDecisionTool handles the recordAccessEntryDecision tool
// Record a decision on an access entry
func (r *Resolver) RecordAccessEntryDecisionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RecordAccessEntryDecisionInput) (*mcp.CallToolResult, types.RecordAccessEntryDecisionOutput, error) {
scope, err := r.Authorize(ctx, input.AccessEntryID, probo.ActionAccessEntryDecide)
func (r *Resolver) RecordAccessReviewEntryDecisionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RecordAccessReviewEntryDecisionInput) (*mcp.CallToolResult, types.RecordAccessReviewEntryDecisionOutput, error) {
scope, err := r.Authorize(ctx, input.AccessReviewEntryID, accessreview.ActionEntryDecide)
if err != nil {
return nil, types.RecordAccessEntryDecisionOutput{}, err
return nil, types.RecordAccessReviewEntryDecisionOutput{}, err
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, types.RecordAccessEntryDecisionOutput{}, fmt.Errorf("no identity in context")
return nil, types.RecordAccessReviewEntryDecisionOutput{}, fmt.Errorf("no identity in context")
}
decisionReq := accessreview.RecordAccessEntryDecisionRequest{
EntryID: input.AccessEntryID,
decisionReq := accessreview.RecordAccessReviewEntryDecisionRequest{
EntryID: input.AccessReviewEntryID,
Decision: input.Decision,
DecisionNote: input.DecisionNote,
}
organizationID, err := r.accessReview.ResolveEntryOrganizationID(ctx, input.AccessEntryID)
organizationID, err := r.accessReview.ResolveEntryOrganizationID(ctx, input.AccessReviewEntryID)
if err == nil {
profile, err := r.iamSvc.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, organizationID)
if err == nil {
@@ -3516,53 +3517,53 @@ func (r *Resolver) RecordAccessEntryDecisionTool(ctx context.Context, req *mcp.C
}
}
entry, err := r.accessReview.Entries(scope).RecordDecision(ctx, decisionReq)
entry, err := r.accessReview.RecordDecision(ctx, scope, decisionReq)
if err != nil {
return nil, types.RecordAccessEntryDecisionOutput{}, fmt.Errorf("cannot record decision: %w", err)
return nil, types.RecordAccessReviewEntryDecisionOutput{}, fmt.Errorf("cannot record decision: %w", err)
}
return nil, types.RecordAccessEntryDecisionOutput{
AccessEntry: types.NewAccessEntry(entry),
return nil, types.RecordAccessReviewEntryDecisionOutput{
AccessEntry: types.NewAccessReviewEntry(entry),
}, nil
}
// RecordAccessEntryDecisionsTool handles the recordAccessEntryDecisions tool
// RecordAccessReviewEntryDecisionsTool handles the recordAccessEntryDecisions tool
// Record decisions on multiple access entries in a single batch
func (r *Resolver) RecordAccessEntryDecisionsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RecordAccessEntryDecisionsInput) (*mcp.CallToolResult, types.RecordAccessEntryDecisionsOutput, error) {
func (r *Resolver) RecordAccessReviewEntryDecisionsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RecordAccessReviewEntryDecisionsInput) (*mcp.CallToolResult, types.RecordAccessReviewEntryDecisionsOutput, error) {
if len(input.Decisions) == 0 {
return nil, types.RecordAccessEntryDecisionsOutput{
AccessEntries: []*types.AccessEntry{},
return nil, types.RecordAccessReviewEntryDecisionsOutput{
AccessReviewEntries: []*types.AccessReviewEntry{},
}, nil
}
const maxBatchSize = 100
if len(input.Decisions) > maxBatchSize {
return nil, types.RecordAccessEntryDecisionsOutput{}, fmt.Errorf("cannot record decisions: batch size %d exceeds maximum of %d", len(input.Decisions), maxBatchSize)
return nil, types.RecordAccessReviewEntryDecisionsOutput{}, fmt.Errorf("cannot record decisions: batch size %d exceeds maximum of %d", len(input.Decisions), maxBatchSize)
}
// Authorize each entry individually to prevent cross-org bypass.
for _, d := range input.Decisions {
if _, err := r.Authorize(ctx, d.AccessEntryID, probo.ActionAccessEntryDecide); err != nil {
return nil, types.RecordAccessEntryDecisionsOutput{}, err
if _, err := r.Authorize(ctx, d.AccessReviewEntryID, accessreview.ActionEntryDecide); err != nil {
return nil, types.RecordAccessReviewEntryDecisionsOutput{}, err
}
}
scope := coredata.NewScopeFromObjectID(input.Decisions[0].AccessEntryID)
scope := coredata.NewScopeFromObjectID(input.Decisions[0].AccessReviewEntryID)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, types.RecordAccessEntryDecisionsOutput{}, fmt.Errorf("no identity in context")
return nil, types.RecordAccessReviewEntryDecisionsOutput{}, fmt.Errorf("no identity in context")
}
// Cache profile lookups per organization so we resolve the correct
// decidedByID for each entry even when a batch spans multiple orgs.
profileCache := make(map[gid.GID]*gid.GID)
decisions := make([]accessreview.RecordAccessEntryDecisionRequest, len(input.Decisions))
decisions := make([]accessreview.RecordAccessReviewEntryDecisionRequest, len(input.Decisions))
for i, d := range input.Decisions {
var decidedByID *gid.GID
organizationID, err := r.accessReview.ResolveEntryOrganizationID(ctx, d.AccessEntryID)
organizationID, err := r.accessReview.ResolveEntryOrganizationID(ctx, d.AccessReviewEntryID)
if err == nil {
if cached, ok := profileCache[organizationID]; ok {
decidedByID = cached
@@ -3576,38 +3577,38 @@ func (r *Resolver) RecordAccessEntryDecisionsTool(ctx context.Context, req *mcp.
}
}
decisions[i] = accessreview.RecordAccessEntryDecisionRequest{
EntryID: d.AccessEntryID,
decisions[i] = accessreview.RecordAccessReviewEntryDecisionRequest{
EntryID: d.AccessReviewEntryID,
Decision: d.Decision,
DecisionNote: d.DecisionNote,
DecidedByID: decidedByID,
}
}
entries, err := r.accessReview.Entries(scope).RecordDecisions(ctx, decisions)
entries, err := r.accessReview.RecordDecisions(ctx, scope, decisions)
if err != nil {
return nil, types.RecordAccessEntryDecisionsOutput{}, fmt.Errorf("cannot record decisions: %w", err)
return nil, types.RecordAccessReviewEntryDecisionsOutput{}, fmt.Errorf("cannot record decisions: %w", err)
}
accessEntries := make([]*types.AccessEntry, len(entries))
accessEntries := make([]*types.AccessReviewEntry, len(entries))
for i, e := range entries {
accessEntries[i] = types.NewAccessEntry(e)
accessEntries[i] = types.NewAccessReviewEntry(e)
}
return nil, types.RecordAccessEntryDecisionsOutput{
AccessEntries: accessEntries,
return nil, types.RecordAccessReviewEntryDecisionsOutput{
AccessReviewEntries: accessEntries,
}, nil
}
// CloseAccessReviewCampaignTool handles the closeAccessReviewCampaign tool
// Close an access review campaign
func (r *Resolver) CloseAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CloseAccessReviewCampaignInput) (*mcp.CallToolResult, types.CloseAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignClose)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignClose)
if err != nil {
return nil, types.CloseAccessReviewCampaignOutput{}, err
}
campaign, err := r.accessReview.Campaigns(scope).Close(ctx, input.CampaignID)
campaign, err := r.accessReview.CloseCampaign(ctx, scope, input.CampaignID)
if err != nil {
return nil, types.CloseAccessReviewCampaignOutput{}, fmt.Errorf("cannot close campaign: %w", err)
}
@@ -3617,21 +3618,21 @@ func (r *Resolver) CloseAccessReviewCampaignTool(ctx context.Context, req *mcp.C
}, nil
}
// ListAccessSourcesTool handles the listAccessSources tool
// ListAccessReviewSourcesTool handles the listAccessSources tool
// List access sources for an organization
func (r *Resolver) ListAccessSourcesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListAccessSourcesInput) (*mcp.CallToolResult, types.ListAccessSourcesOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, probo.ActionAccessSourceList)
func (r *Resolver) ListAccessReviewSourcesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListAccessReviewSourcesInput) (*mcp.CallToolResult, types.ListAccessReviewSourcesOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, accessreview.ActionSourceList)
if err != nil {
return nil, types.ListAccessSourcesOutput{}, err
return nil, types.ListAccessReviewSourcesOutput{}, err
}
pageOrderBy := page.OrderBy[coredata.AccessSourceOrderField]{
Field: coredata.AccessSourceOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.AccessReviewSourceOrderField]{
Field: coredata.AccessReviewSourceOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if input.OrderBy != nil {
pageOrderBy = page.OrderBy[coredata.AccessSourceOrderField]{
pageOrderBy = page.OrderBy[coredata.AccessReviewSourceOrderField]{
Field: input.OrderBy.Field,
Direction: input.OrderBy.Direction,
}
@@ -3639,56 +3640,56 @@ func (r *Resolver) ListAccessSourcesTool(ctx context.Context, req *mcp.CallToolR
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
p, err := r.accessReview.Sources(scope).ListForOrganizationID(ctx, input.OrganizationID, cursor)
p, err := r.accessReview.ListSourcesForOrganizationID(ctx, scope, input.OrganizationID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list access sources: %w", err))
}
return nil, types.NewListAccessSourcesOutput(p), nil
return nil, types.NewListAccessReviewSourcesOutput(p), nil
}
// CreateAccessSourceTool handles the createAccessSource tool
// CreateAccessReviewSourceTool handles the createAccessSource tool
// Create a new access source for an organization
func (r *Resolver) CreateAccessSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateAccessSourceInput) (*mcp.CallToolResult, types.CreateAccessSourceOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, probo.ActionAccessSourceCreate)
func (r *Resolver) CreateAccessReviewSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateAccessReviewSourceInput) (*mcp.CallToolResult, types.CreateAccessReviewSourceOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, accessreview.ActionSourceCreate)
if err != nil {
return nil, types.CreateAccessSourceOutput{}, err
return nil, types.CreateAccessReviewSourceOutput{}, err
}
source, err := r.accessReview.Sources(scope).Create(ctx, accessreview.CreateAccessSourceRequest{
source, err := r.accessReview.CreateSource(ctx, scope, accessreview.CreateAccessReviewSourceRequest{
OrganizationID: input.OrganizationID,
ConnectorID: input.ConnectorID,
Name: input.Name,
Category: coredata.AccessSourceCategorySaaS,
Category: coredata.AccessReviewSourceCategorySaaS,
CsvData: input.CsvData,
})
if err != nil {
return nil, types.CreateAccessSourceOutput{}, fmt.Errorf("cannot create access source: %w", err)
return nil, types.CreateAccessReviewSourceOutput{}, fmt.Errorf("cannot create access source: %w", err)
}
return nil, types.CreateAccessSourceOutput{
AccessSource: types.NewAccessSource(source),
return nil, types.CreateAccessReviewSourceOutput{
AccessReviewSource: types.NewAccessReviewSource(source),
}, nil
}
// UpdateAccessSourceTool handles the updateAccessSource tool
// UpdateAccessReviewSourceTool handles the updateAccessSource tool
// Update an existing access source
func (r *Resolver) UpdateAccessSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateAccessSourceInput) (*mcp.CallToolResult, types.UpdateAccessSourceOutput, error) {
scope, err := r.Authorize(ctx, input.AccessSourceID, probo.ActionAccessSourceUpdate)
func (r *Resolver) UpdateAccessReviewSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateAccessReviewSourceInput) (*mcp.CallToolResult, types.UpdateAccessReviewSourceOutput, error) {
scope, err := r.Authorize(ctx, input.AccessReviewSourceID, accessreview.ActionSourceUpdate)
if err != nil {
return nil, types.UpdateAccessSourceOutput{}, err
return nil, types.UpdateAccessReviewSourceOutput{}, err
}
updateReq := accessreview.UpdateAccessSourceRequest{
AccessSourceID: input.AccessSourceID,
Name: input.Name,
updateReq := accessreview.UpdateAccessReviewSourceRequest{
AccessReviewSourceID: input.AccessReviewSourceID,
Name: input.Name,
}
if rawConnectorID := UnwrapOmittable(input.ConnectorID); rawConnectorID != nil {
if *rawConnectorID != nil {
id, err := gid.ParseGID(**rawConnectorID)
if err != nil {
return nil, types.UpdateAccessSourceOutput{}, fmt.Errorf("cannot parse connector_id: %w", err)
return nil, types.UpdateAccessReviewSourceOutput{}, fmt.Errorf("cannot parse connector_id: %w", err)
}
idPtr := &id
@@ -3704,37 +3705,37 @@ func (r *Resolver) UpdateAccessSourceTool(ctx context.Context, req *mcp.CallTool
updateReq.CsvData = rawCsvData
}
source, err := r.accessReview.Sources(scope).Update(ctx, updateReq)
source, err := r.accessReview.UpdateSource(ctx, scope, updateReq)
if err != nil {
return nil, types.UpdateAccessSourceOutput{}, fmt.Errorf("cannot update access source: %w", err)
return nil, types.UpdateAccessReviewSourceOutput{}, fmt.Errorf("cannot update access source: %w", err)
}
return nil, types.UpdateAccessSourceOutput{
AccessSource: types.NewAccessSource(source),
return nil, types.UpdateAccessReviewSourceOutput{
AccessReviewSource: types.NewAccessReviewSource(source),
}, nil
}
// DeleteAccessSourceTool handles the deleteAccessSource tool
// DeleteAccessReviewSourceTool handles the deleteAccessSource tool
// Delete an access source
func (r *Resolver) DeleteAccessSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteAccessSourceInput) (*mcp.CallToolResult, types.DeleteAccessSourceOutput, error) {
scope, err := r.Authorize(ctx, input.AccessSourceID, probo.ActionAccessSourceDelete)
func (r *Resolver) DeleteAccessReviewSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteAccessReviewSourceInput) (*mcp.CallToolResult, types.DeleteAccessReviewSourceOutput, error) {
scope, err := r.Authorize(ctx, input.AccessReviewSourceID, accessreview.ActionSourceDelete)
if err != nil {
return nil, types.DeleteAccessSourceOutput{}, err
return nil, types.DeleteAccessReviewSourceOutput{}, err
}
if err := r.accessReview.Sources(scope).Delete(ctx, input.AccessSourceID); err != nil {
return nil, types.DeleteAccessSourceOutput{}, fmt.Errorf("cannot delete access source: %w", err)
if err := r.accessReview.DeleteSource(ctx, scope, input.AccessReviewSourceID); err != nil {
return nil, types.DeleteAccessReviewSourceOutput{}, fmt.Errorf("cannot delete access source: %w", err)
}
return nil, types.DeleteAccessSourceOutput{
DeletedAccessSourceID: input.AccessSourceID,
return nil, types.DeleteAccessReviewSourceOutput{
DeletedAccessReviewSourceID: input.AccessReviewSourceID,
}, nil
}
// CreateAccessReviewCampaignTool handles the createAccessReviewCampaign tool
// Create a new access review campaign for an organization
func (r *Resolver) CreateAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateAccessReviewCampaignInput) (*mcp.CallToolResult, types.CreateAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.OrganizationID, probo.ActionAccessReviewCampaignCreate)
scope, err := r.Authorize(ctx, input.OrganizationID, accessreview.ActionCampaignCreate)
if err != nil {
return nil, types.CreateAccessReviewCampaignOutput{}, err
}
@@ -3744,12 +3745,12 @@ func (r *Resolver) CreateAccessReviewCampaignTool(ctx context.Context, req *mcp.
description = *input.Description
}
campaign, err := r.accessReview.Campaigns(scope).Create(ctx, accessreview.CreateAccessReviewCampaignRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: description,
FrameworkControls: input.FrameworkControls,
AccessSourceIDs: input.AccessSourceIds,
campaign, err := r.accessReview.CreateCampaign(ctx, scope, accessreview.CreateAccessReviewCampaignRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: description,
FrameworkControls: input.FrameworkControls,
AccessReviewSourceIDs: input.AccessReviewSourceIds,
})
if err != nil {
return nil, types.CreateAccessReviewCampaignOutput{}, fmt.Errorf("cannot create access review campaign: %w", err)
@@ -3763,7 +3764,7 @@ func (r *Resolver) CreateAccessReviewCampaignTool(ctx context.Context, req *mcp.
// UpdateAccessReviewCampaignTool handles the updateAccessReviewCampaign tool
// Update an existing access review campaign
func (r *Resolver) UpdateAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateAccessReviewCampaignInput) (*mcp.CallToolResult, types.UpdateAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignUpdate)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignUpdate)
if err != nil {
return nil, types.UpdateAccessReviewCampaignOutput{}, err
}
@@ -3790,7 +3791,7 @@ func (r *Resolver) UpdateAccessReviewCampaignTool(ctx context.Context, req *mcp.
}
}
campaign, err := r.accessReview.Campaigns(scope).Update(ctx, updateReq)
campaign, err := r.accessReview.UpdateCampaign(ctx, scope, updateReq)
if err != nil {
return nil, types.UpdateAccessReviewCampaignOutput{}, fmt.Errorf("cannot update access review campaign: %w", err)
}
@@ -3803,12 +3804,12 @@ func (r *Resolver) UpdateAccessReviewCampaignTool(ctx context.Context, req *mcp.
// DeleteAccessReviewCampaignTool handles the deleteAccessReviewCampaign tool
// Delete an access review campaign
func (r *Resolver) DeleteAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteAccessReviewCampaignInput) (*mcp.CallToolResult, types.DeleteAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignDelete)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignDelete)
if err != nil {
return nil, types.DeleteAccessReviewCampaignOutput{}, err
}
if err := r.accessReview.Campaigns(scope).Delete(ctx, input.CampaignID); err != nil {
if err := r.accessReview.DeleteCampaign(ctx, scope, input.CampaignID); err != nil {
return nil, types.DeleteAccessReviewCampaignOutput{}, fmt.Errorf("cannot delete access review campaign: %w", err)
}
@@ -3820,12 +3821,12 @@ func (r *Resolver) DeleteAccessReviewCampaignTool(ctx context.Context, req *mcp.
// StartAccessReviewCampaignTool handles the startAccessReviewCampaign tool
// Start an access review campaign
func (r *Resolver) StartAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.StartAccessReviewCampaignInput) (*mcp.CallToolResult, types.StartAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignStart)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignStart)
if err != nil {
return nil, types.StartAccessReviewCampaignOutput{}, err
}
campaign, err := r.accessReview.Campaigns(scope).Start(ctx, input.CampaignID)
campaign, err := r.accessReview.StartCampaign(ctx, scope, input.CampaignID)
if err != nil {
return nil, types.StartAccessReviewCampaignOutput{}, fmt.Errorf("cannot start access review campaign: %w", err)
}
@@ -3838,12 +3839,12 @@ func (r *Resolver) StartAccessReviewCampaignTool(ctx context.Context, req *mcp.C
// CancelAccessReviewCampaignTool handles the cancelAccessReviewCampaign tool
// Cancel an in-progress access review campaign
func (r *Resolver) CancelAccessReviewCampaignTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CancelAccessReviewCampaignInput) (*mcp.CallToolResult, types.CancelAccessReviewCampaignOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignCancel)
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignCancel)
if err != nil {
return nil, types.CancelAccessReviewCampaignOutput{}, err
}
campaign, err := r.accessReview.Campaigns(scope).Cancel(ctx, input.CampaignID)
campaign, err := r.accessReview.CancelCampaign(ctx, scope, input.CampaignID)
if err != nil {
return nil, types.CancelAccessReviewCampaignOutput{}, fmt.Errorf("cannot cancel access review campaign: %w", err)
}
@@ -3853,67 +3854,67 @@ func (r *Resolver) CancelAccessReviewCampaignTool(ctx context.Context, req *mcp.
}, nil
}
// AddAccessReviewCampaignScopeSourceTool handles the addAccessReviewCampaignScopeSource tool
// AddAccessReviewCampaignSourceTool handles the addAccessReviewCampaignScopeSource tool
// Add an access source to an access review campaign's scope
func (r *Resolver) AddAccessReviewCampaignScopeSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddAccessReviewCampaignScopeSourceInput) (*mcp.CallToolResult, types.AddAccessReviewCampaignScopeSourceOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignAddScopeSource)
func (r *Resolver) AddAccessReviewCampaignSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddAccessReviewCampaignSourceInput) (*mcp.CallToolResult, types.AddAccessReviewCampaignSourceOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignAddSource)
if err != nil {
return nil, types.AddAccessReviewCampaignScopeSourceOutput{}, err
return nil, types.AddAccessReviewCampaignSourceOutput{}, err
}
campaign, err := r.accessReview.Campaigns(scope).AddScopeSource(ctx, accessreview.AddCampaignScopeSourceRequest{
CampaignID: input.CampaignID,
AccessSourceID: input.AccessSourceID,
campaign, err := r.accessReview.AddCampaignSource(ctx, scope, accessreview.AddCampaignSourceRequest{
CampaignID: input.CampaignID,
AccessReviewSourceID: input.AccessReviewSourceID,
})
if err != nil {
return nil, types.AddAccessReviewCampaignScopeSourceOutput{}, fmt.Errorf("cannot add scope source to access review campaign: %w", err)
return nil, types.AddAccessReviewCampaignSourceOutput{}, fmt.Errorf("cannot add scope source to access review campaign: %w", err)
}
return nil, types.AddAccessReviewCampaignScopeSourceOutput{
return nil, types.AddAccessReviewCampaignSourceOutput{
Campaign: types.NewAccessReviewCampaign(campaign),
}, nil
}
// RemoveAccessReviewCampaignScopeSourceTool handles the removeAccessReviewCampaignScopeSource tool
// RemoveAccessReviewCampaignSourceTool handles the removeAccessReviewCampaignScopeSource tool
// Remove an access source from an access review campaign's scope
func (r *Resolver) RemoveAccessReviewCampaignScopeSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RemoveAccessReviewCampaignScopeSourceInput) (*mcp.CallToolResult, types.RemoveAccessReviewCampaignScopeSourceOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, probo.ActionAccessReviewCampaignRemoveScopeSource)
func (r *Resolver) RemoveAccessReviewCampaignSourceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RemoveAccessReviewCampaignSourceInput) (*mcp.CallToolResult, types.RemoveAccessReviewCampaignSourceOutput, error) {
scope, err := r.Authorize(ctx, input.CampaignID, accessreview.ActionCampaignRemoveSource)
if err != nil {
return nil, types.RemoveAccessReviewCampaignScopeSourceOutput{}, err
return nil, types.RemoveAccessReviewCampaignSourceOutput{}, err
}
campaign, err := r.accessReview.Campaigns(scope).RemoveScopeSource(ctx, accessreview.RemoveCampaignScopeSourceRequest{
CampaignID: input.CampaignID,
AccessSourceID: input.AccessSourceID,
campaign, err := r.accessReview.RemoveCampaignSource(ctx, scope, accessreview.RemoveCampaignSourceRequest{
CampaignID: input.CampaignID,
AccessReviewSourceID: input.AccessReviewSourceID,
})
if err != nil {
return nil, types.RemoveAccessReviewCampaignScopeSourceOutput{}, fmt.Errorf("cannot remove scope source from access review campaign: %w", err)
return nil, types.RemoveAccessReviewCampaignSourceOutput{}, fmt.Errorf("cannot remove scope source from access review campaign: %w", err)
}
return nil, types.RemoveAccessReviewCampaignScopeSourceOutput{
return nil, types.RemoveAccessReviewCampaignSourceOutput{
Campaign: types.NewAccessReviewCampaign(campaign),
}, nil
}
// FlagAccessEntryTool handles the flagAccessEntry tool
// FlagAccessReviewEntryTool handles the flagAccessEntry tool
// Flag an access entry during review
func (r *Resolver) FlagAccessEntryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.FlagAccessEntryInput) (*mcp.CallToolResult, types.FlagAccessEntryOutput, error) {
scope, err := r.Authorize(ctx, input.AccessEntryID, probo.ActionAccessEntryFlag)
func (r *Resolver) FlagAccessReviewEntryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.FlagAccessReviewEntryInput) (*mcp.CallToolResult, types.FlagAccessReviewEntryOutput, error) {
scope, err := r.Authorize(ctx, input.AccessReviewEntryID, accessreview.ActionEntryFlag)
if err != nil {
return nil, types.FlagAccessEntryOutput{}, err
return nil, types.FlagAccessReviewEntryOutput{}, err
}
entry, err := r.accessReview.Entries(scope).FlagEntry(ctx, accessreview.FlagAccessEntryRequest{
EntryID: input.AccessEntryID,
entry, err := r.accessReview.FlagEntry(ctx, scope, accessreview.FlagAccessReviewEntryRequest{
EntryID: input.AccessReviewEntryID,
Flags: input.Flags,
FlagReasons: input.FlagReasons,
})
if err != nil {
return nil, types.FlagAccessEntryOutput{}, fmt.Errorf("cannot flag access entry: %w", err)
return nil, types.FlagAccessReviewEntryOutput{}, fmt.Errorf("cannot flag access entry: %w", err)
}
return nil, types.FlagAccessEntryOutput{
AccessEntry: types.NewAccessEntry(entry),
return nil, types.FlagAccessReviewEntryOutput{
AccessEntry: types.NewAccessReviewEntry(entry),
}, nil
}

View File

@@ -7524,7 +7524,7 @@ components:
- CANCELLED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatus
AccessEntryDecision:
AccessReviewEntryDecision:
type: string
enum:
- PENDING
@@ -7532,9 +7532,9 @@ components:
- REVOKE
- DEFER
- ESCALATE
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryDecision
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryDecision
AccessEntryFlag:
AccessReviewEntryFlag:
type: string
enum:
- NONE
@@ -7552,17 +7552,17 @@ components:
- NO_BUSINESS_JUSTIFICATION
- OUT_OF_DEPARTMENT
- SHARED_ACCOUNT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryFlag
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryFlag
AccessEntryIncrementalTag:
AccessReviewEntryIncrementalTag:
type: string
enum:
- NEW
- REMOVED
- UNCHANGED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryIncrementalTag
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryIncrementalTag
AccessEntryAuthMethod:
AccessReviewEntryAuthMethod:
type: string
enum:
- SSO
@@ -7570,14 +7570,14 @@ components:
- API_KEY
- SERVICE_ACCOUNT
- UNKNOWN
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethod
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryAuthMethod
AccessEntryAccountType:
AccessReviewEntryAccountType:
type: string
enum:
- USER
- SERVICE_ACCOUNT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryAccountType
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryAccountType
MFAStatus:
type: string
@@ -7606,20 +7606,20 @@ components:
$ref: "#/components/schemas/OrderDirection"
description: Order direction
AccessEntryOrderField:
AccessReviewEntryOrderField:
type: string
enum:
- CREATED_AT
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessEntryOrderField
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewEntryOrderField
AccessEntryOrderBy:
AccessReviewEntryOrderBy:
type: object
required:
- field
- direction
properties:
field:
$ref: "#/components/schemas/AccessEntryOrderField"
$ref: "#/components/schemas/AccessReviewEntryOrderField"
description: Order field
direction:
$ref: "#/components/schemas/OrderDirection"
@@ -7676,12 +7676,12 @@ components:
format: date-time
description: Update timestamp
AccessEntry:
AccessReviewEntry:
type: object
required:
- id
- campaign_id
- access_source_id
- access_review_campaign_source_id
- email
- full_name
- role
@@ -7703,9 +7703,9 @@ components:
campaign_id:
$ref: "#/components/schemas/GID"
description: Campaign ID
access_source_id:
access_review_campaign_source_id:
$ref: "#/components/schemas/GID"
description: Access source ID
description: Per-campaign source snapshot ID
email:
type: string
description: User email
@@ -7730,10 +7730,10 @@ components:
$ref: "#/components/schemas/MFAStatus"
description: MFA status
auth_method:
$ref: "#/components/schemas/AccessEntryAuthMethod"
$ref: "#/components/schemas/AccessReviewEntryAuthMethod"
description: Authentication method
account_type:
$ref: "#/components/schemas/AccessEntryAccountType"
$ref: "#/components/schemas/AccessReviewEntryAccountType"
description: Account type (user or service account)
last_login:
type:
@@ -7751,12 +7751,12 @@ components:
type: string
description: External ID in the source system
incremental_tag:
$ref: "#/components/schemas/AccessEntryIncrementalTag"
$ref: "#/components/schemas/AccessReviewEntryIncrementalTag"
description: Change tag compared to previous campaign
flags:
type: array
items:
$ref: "#/components/schemas/AccessEntryFlag"
$ref: "#/components/schemas/AccessReviewEntryFlag"
description: Risk flags
flag_reasons:
type: array
@@ -7764,7 +7764,7 @@ components:
type: string
description: Reasons for the flags
decision:
$ref: "#/components/schemas/AccessEntryDecision"
$ref: "#/components/schemas/AccessReviewEntryDecision"
description: Review decision
decision_note:
type:
@@ -7791,7 +7791,7 @@ components:
format: date-time
description: Update timestamp
AccessEntryStatistics:
AccessReviewStatistics:
type: object
required:
- total_count
@@ -7857,11 +7857,11 @@ components:
campaign_id:
$ref: "#/components/schemas/GID"
description: Campaign ID
access_source_id:
access_review_campaign_source_id:
$ref: "#/components/schemas/GID"
description: Filter by access source ID
description: Filter by per-campaign source snapshot ID
order_by:
$ref: "#/components/schemas/AccessEntryOrderBy"
$ref: "#/components/schemas/AccessReviewEntryOrderBy"
description: Order by
size:
type: integer
@@ -7873,13 +7873,13 @@ components:
type: object
properties:
decision:
$ref: "#/components/schemas/AccessEntryDecision"
$ref: "#/components/schemas/AccessReviewEntryDecision"
description: Filter by decision status
flag:
$ref: "#/components/schemas/AccessEntryFlag"
$ref: "#/components/schemas/AccessReviewEntryFlag"
description: Filter by flag
incremental_tag:
$ref: "#/components/schemas/AccessEntryIncrementalTag"
$ref: "#/components/schemas/AccessReviewEntryIncrementalTag"
description: Filter by incremental tag
is_admin:
type: boolean
@@ -7888,10 +7888,10 @@ components:
type: boolean
description: Filter by active status at the source
auth_method:
$ref: "#/components/schemas/AccessEntryAuthMethod"
$ref: "#/components/schemas/AccessReviewEntryAuthMethod"
description: Filter by auth method
account_type:
$ref: "#/components/schemas/AccessEntryAccountType"
$ref: "#/components/schemas/AccessReviewEntryAccountType"
description: Filter by account type
ListAccessEntriesOutput:
@@ -7905,9 +7905,9 @@ components:
entries:
type: array
items:
$ref: "#/components/schemas/AccessEntry"
$ref: "#/components/schemas/AccessReviewEntry"
GetAccessReviewCampaignStatisticsInput:
GetAccessReviewStatisticsInput:
type: object
required:
- campaign_id
@@ -7916,39 +7916,39 @@ components:
$ref: "#/components/schemas/GID"
description: Campaign ID
GetAccessReviewCampaignStatisticsOutput:
GetAccessReviewStatisticsOutput:
type: object
required:
- statistics
properties:
statistics:
$ref: "#/components/schemas/AccessEntryStatistics"
$ref: "#/components/schemas/AccessReviewStatistics"
RecordAccessEntryDecisionMCPInput:
RecordAccessReviewEntryDecisionMCPInput:
type: object
required:
- access_entry_id
- access_review_entry_id
- decision
properties:
access_entry_id:
access_review_entry_id:
$ref: "#/components/schemas/GID"
description: Access entry ID
decision:
$ref: "#/components/schemas/AccessEntryDecision"
$ref: "#/components/schemas/AccessReviewEntryDecision"
description: Decision (APPROVED, REVOKE, DEFER, ESCALATE)
decision_note:
type: string
description: Decision justification (required for non-APPROVED decisions)
RecordAccessEntryDecisionMCPOutput:
RecordAccessReviewEntryDecisionMCPOutput:
type: object
required:
- access_entry
properties:
access_entry:
$ref: "#/components/schemas/AccessEntry"
$ref: "#/components/schemas/AccessReviewEntry"
RecordAccessEntryDecisionsMCPInput:
RecordAccessReviewEntryDecisionsMCPInput:
type: object
required:
- decisions
@@ -7958,42 +7958,42 @@ components:
items:
type: object
required:
- access_entry_id
- access_review_entry_id
- decision
properties:
access_entry_id:
access_review_entry_id:
$ref: "#/components/schemas/GID"
description: Access entry ID
decision:
$ref: "#/components/schemas/AccessEntryDecision"
$ref: "#/components/schemas/AccessReviewEntryDecision"
description: Decision (APPROVED, REVOKE, DEFER, ESCALATE)
decision_note:
type: string
description: Decision justification (required for non-APPROVED decisions)
RecordAccessEntryDecisionsMCPOutput:
RecordAccessReviewEntryDecisionsMCPOutput:
type: object
required:
- access_entries
- access_review_entries
properties:
access_entries:
access_review_entries:
type: array
items:
$ref: "#/components/schemas/AccessEntry"
$ref: "#/components/schemas/AccessReviewEntry"
FlagAccessEntryMCPInput:
FlagAccessReviewEntryMCPInput:
type: object
required:
- access_entry_id
- access_review_entry_id
- flags
properties:
access_entry_id:
access_review_entry_id:
$ref: "#/components/schemas/GID"
description: Access entry ID
flags:
type: array
items:
$ref: "#/components/schemas/AccessEntryFlag"
$ref: "#/components/schemas/AccessReviewEntryFlag"
description: Flags to set (ORPHANED, INACTIVE, EXCESSIVE, ROLE_MISMATCH, NEW, DORMANT, TERMINATED_USER, CONTRACTOR_EXPIRED, SOD_CONFLICT, PRIVILEGED_ACCESS, ROLE_CREEP, NO_BUSINESS_JUSTIFICATION, OUT_OF_DEPARTMENT, SHARED_ACCOUNT)
flag_reasons:
type: array
@@ -8001,13 +8001,13 @@ components:
type: string
description: Reasons for flagging
FlagAccessEntryMCPOutput:
FlagAccessReviewEntryMCPOutput:
type: object
required:
- access_entry
properties:
access_entry:
$ref: "#/components/schemas/AccessEntry"
$ref: "#/components/schemas/AccessReviewEntry"
CloseAccessReviewCampaignMCPInput:
type: object
@@ -8026,35 +8026,35 @@ components:
campaign:
$ref: "#/components/schemas/AccessReviewCampaign"
AccessSourceCategory:
AccessReviewSourceCategory:
type: string
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessSourceCategory
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewSourceCategory
enum:
- SAAS
- CLOUD_INFRA
- SOURCE_CODE
- OTHER
AccessSourceOrderField:
AccessReviewSourceOrderField:
type: string
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessSourceOrderField
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.AccessReviewSourceOrderField
enum:
- CREATED_AT
AccessSourceOrderBy:
AccessReviewSourceOrderBy:
type: object
required:
- field
- direction
properties:
field:
$ref: "#/components/schemas/AccessSourceOrderField"
$ref: "#/components/schemas/AccessReviewSourceOrderField"
description: Order field
direction:
$ref: "#/components/schemas/OrderDirection"
description: Order direction
AccessSource:
AccessReviewSource:
type: object
required:
- id
@@ -8091,7 +8091,7 @@ components:
format: date-time
description: Update timestamp
ListAccessSourcesInput:
ListAccessReviewSourcesInput:
type: object
required:
- organization_id
@@ -8100,7 +8100,7 @@ components:
$ref: "#/components/schemas/GID"
description: Organization ID
order_by:
$ref: "#/components/schemas/AccessSourceOrderBy"
$ref: "#/components/schemas/AccessReviewSourceOrderBy"
description: Order by
size:
type: integer
@@ -8109,20 +8109,20 @@ components:
$ref: "#/components/schemas/CursorKey"
description: Page cursor
ListAccessSourcesOutput:
ListAccessReviewSourcesOutput:
type: object
required:
- access_sources
- access_review_sources
properties:
next_cursor:
$ref: "#/components/schemas/CursorKey"
description: Next cursor
access_sources:
access_review_sources:
type: array
items:
$ref: "#/components/schemas/AccessSource"
$ref: "#/components/schemas/AccessReviewSource"
CreateAccessSourceMCPInput:
CreateAccessReviewSourceMCPInput:
type: object
required:
- organization_id
@@ -8141,20 +8141,20 @@ components:
type: string
description: CSV data for manual sources (optional)
CreateAccessSourceMCPOutput:
CreateAccessReviewSourceMCPOutput:
type: object
required:
- access_source
properties:
access_source:
$ref: "#/components/schemas/AccessSource"
access_review_source:
$ref: "#/components/schemas/AccessReviewSource"
UpdateAccessSourceMCPInput:
UpdateAccessReviewSourceMCPInput:
type: object
required:
- access_source_id
- access_review_source_id
properties:
access_source_id:
access_review_source_id:
$ref: "#/components/schemas/GID"
description: Access source ID
name:
@@ -8173,29 +8173,29 @@ components:
go.probo.inc/mcpgen/omittable: true
description: CSV data for manual sources (set to null to remove)
UpdateAccessSourceMCPOutput:
UpdateAccessReviewSourceMCPOutput:
type: object
required:
- access_source
properties:
access_source:
$ref: "#/components/schemas/AccessSource"
access_review_source:
$ref: "#/components/schemas/AccessReviewSource"
DeleteAccessSourceMCPInput:
DeleteAccessReviewSourceMCPInput:
type: object
required:
- access_source_id
- access_review_source_id
properties:
access_source_id:
access_review_source_id:
$ref: "#/components/schemas/GID"
description: Access source ID
DeleteAccessSourceMCPOutput:
DeleteAccessReviewSourceMCPOutput:
type: object
required:
- deleted_access_source_id
- deleted_access_review_source_id
properties:
deleted_access_source_id:
deleted_access_review_source_id:
$ref: "#/components/schemas/GID"
description: Deleted access source ID
@@ -8219,7 +8219,7 @@ components:
items:
type: string
description: Framework control references
access_source_ids:
access_review_source_ids:
type: array
items:
$ref: "#/components/schemas/GID"
@@ -8316,20 +8316,20 @@ components:
campaign:
$ref: "#/components/schemas/AccessReviewCampaign"
AddAccessReviewCampaignScopeSourceMCPInput:
AddAccessReviewCampaignSourceMCPInput:
type: object
required:
- campaign_id
- access_source_id
- access_review_source_id
properties:
campaign_id:
$ref: "#/components/schemas/GID"
description: Campaign ID
access_source_id:
access_review_source_id:
$ref: "#/components/schemas/GID"
description: Access source ID to add to scope
AddAccessReviewCampaignScopeSourceMCPOutput:
AddAccessReviewCampaignSourceMCPOutput:
type: object
required:
- campaign
@@ -8337,20 +8337,20 @@ components:
campaign:
$ref: "#/components/schemas/AccessReviewCampaign"
RemoveAccessReviewCampaignScopeSourceMCPInput:
RemoveAccessReviewCampaignSourceMCPInput:
type: object
required:
- campaign_id
- access_source_id
- access_review_source_id
properties:
campaign_id:
$ref: "#/components/schemas/GID"
description: Campaign ID
access_source_id:
access_review_source_id:
$ref: "#/components/schemas/GID"
description: Access source ID to remove from scope
RemoveAccessReviewCampaignScopeSourceMCPOutput:
RemoveAccessReviewCampaignSourceMCPOutput:
type: object
required:
- campaign
@@ -13310,39 +13310,39 @@ tools:
$ref: "#/components/schemas/ListAccessEntriesInput"
outputSchema:
$ref: "#/components/schemas/ListAccessEntriesOutput"
- name: getAccessReviewCampaignStatistics
- name: getAccessReviewStatistics
description: Get statistics for an access review campaign including counts by decision, flag, and incremental tag
hints:
readonly: true
idempotent: true
inputSchema:
$ref: "#/components/schemas/GetAccessReviewCampaignStatisticsInput"
$ref: "#/components/schemas/GetAccessReviewStatisticsInput"
outputSchema:
$ref: "#/components/schemas/GetAccessReviewCampaignStatisticsOutput"
- name: recordAccessEntryDecision
$ref: "#/components/schemas/GetAccessReviewStatisticsOutput"
- name: recordAccessReviewEntryDecision
description: Record a decision on an access entry (APPROVED, REVOKE, DEFER, or ESCALATE). Non-APPROVED decisions require a decision_note.
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/RecordAccessEntryDecisionMCPInput"
$ref: "#/components/schemas/RecordAccessReviewEntryDecisionMCPInput"
outputSchema:
$ref: "#/components/schemas/RecordAccessEntryDecisionMCPOutput"
- name: recordAccessEntryDecisions
$ref: "#/components/schemas/RecordAccessReviewEntryDecisionMCPOutput"
- name: recordAccessReviewEntryDecisions
description: Record decisions on multiple access entries in a single batch. Non-APPROVED decisions require a decision_note.
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/RecordAccessEntryDecisionsMCPInput"
$ref: "#/components/schemas/RecordAccessReviewEntryDecisionsMCPInput"
outputSchema:
$ref: "#/components/schemas/RecordAccessEntryDecisionsMCPOutput"
- name: flagAccessEntry
$ref: "#/components/schemas/RecordAccessReviewEntryDecisionsMCPOutput"
- name: flagAccessReviewEntry
description: Flag an access entry with one or more flags during review (ORPHANED, INACTIVE, EXCESSIVE, ROLE_MISMATCH, NEW, etc.). Optionally provide reasons.
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/FlagAccessEntryMCPInput"
$ref: "#/components/schemas/FlagAccessReviewEntryMCPInput"
outputSchema:
$ref: "#/components/schemas/FlagAccessEntryMCPOutput"
$ref: "#/components/schemas/FlagAccessReviewEntryMCPOutput"
- name: closeAccessReviewCampaign
description: Close an access review campaign. All entries must have been decided (no PENDING entries).
hints:
@@ -13351,40 +13351,40 @@ tools:
$ref: "#/components/schemas/CloseAccessReviewCampaignMCPInput"
outputSchema:
$ref: "#/components/schemas/CloseAccessReviewCampaignMCPOutput"
- name: listAccessSources
- name: listAccessReviewSources
description: List access sources for an organization
hints:
readonly: true
idempotent: true
inputSchema:
$ref: "#/components/schemas/ListAccessSourcesInput"
$ref: "#/components/schemas/ListAccessReviewSourcesInput"
outputSchema:
$ref: "#/components/schemas/ListAccessSourcesOutput"
- name: createAccessSource
$ref: "#/components/schemas/ListAccessReviewSourcesOutput"
- name: createAccessReviewSource
description: Create a new access source for an organization
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/CreateAccessSourceMCPInput"
$ref: "#/components/schemas/CreateAccessReviewSourceMCPInput"
outputSchema:
$ref: "#/components/schemas/CreateAccessSourceMCPOutput"
- name: updateAccessSource
$ref: "#/components/schemas/CreateAccessReviewSourceMCPOutput"
- name: updateAccessReviewSource
description: Update an existing access source
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/UpdateAccessSourceMCPInput"
$ref: "#/components/schemas/UpdateAccessReviewSourceMCPInput"
outputSchema:
$ref: "#/components/schemas/UpdateAccessSourceMCPOutput"
- name: deleteAccessSource
$ref: "#/components/schemas/UpdateAccessReviewSourceMCPOutput"
- name: deleteAccessReviewSource
description: Delete an access source
hints:
readonly: false
destructive: true
inputSchema:
$ref: "#/components/schemas/DeleteAccessSourceMCPInput"
$ref: "#/components/schemas/DeleteAccessReviewSourceMCPInput"
outputSchema:
$ref: "#/components/schemas/DeleteAccessSourceMCPOutput"
$ref: "#/components/schemas/DeleteAccessReviewSourceMCPOutput"
- name: createAccessReviewCampaign
description: Create a new access review campaign for an organization
hints:
@@ -13426,22 +13426,22 @@ tools:
$ref: "#/components/schemas/CancelAccessReviewCampaignMCPInput"
outputSchema:
$ref: "#/components/schemas/CancelAccessReviewCampaignMCPOutput"
- name: addAccessReviewCampaignScopeSource
- name: addAccessReviewCampaignSource
description: Add an access source to an access review campaign's scope
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/AddAccessReviewCampaignScopeSourceMCPInput"
$ref: "#/components/schemas/AddAccessReviewCampaignSourceMCPInput"
outputSchema:
$ref: "#/components/schemas/AddAccessReviewCampaignScopeSourceMCPOutput"
- name: removeAccessReviewCampaignScopeSource
$ref: "#/components/schemas/AddAccessReviewCampaignSourceMCPOutput"
- name: removeAccessReviewCampaignSource
description: Remove an access source from an access review campaign's scope
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/RemoveAccessReviewCampaignScopeSourceMCPInput"
$ref: "#/components/schemas/RemoveAccessReviewCampaignSourceMCPInput"
outputSchema:
$ref: "#/components/schemas/RemoveAccessReviewCampaignScopeSourceMCPOutput"
$ref: "#/components/schemas/RemoveAccessReviewCampaignSourceMCPOutput"
- name: getOrganizationContext
description: Get the organization context containing structured sections about the company
hints:

View File

@@ -19,8 +19,8 @@ import (
"go.probo.inc/probo/pkg/page"
)
func NewAccessSource(s *coredata.AccessSource) *AccessSource {
return &AccessSource{
func NewAccessReviewSource(s *coredata.AccessReviewSource) *AccessReviewSource {
return &AccessReviewSource{
ID: s.ID,
OrganizationID: s.OrganizationID,
ConnectorID: s.ConnectorID,
@@ -31,12 +31,12 @@ func NewAccessSource(s *coredata.AccessSource) *AccessSource {
}
}
func NewListAccessSourcesOutput(
p *page.Page[*coredata.AccessSource, coredata.AccessSourceOrderField],
) ListAccessSourcesOutput {
sources := make([]*AccessSource, 0, len(p.Data))
func NewListAccessReviewSourcesOutput(
p *page.Page[*coredata.AccessReviewSource, coredata.AccessReviewSourceOrderField],
) ListAccessReviewSourcesOutput {
sources := make([]*AccessReviewSource, 0, len(p.Data))
for _, s := range p.Data {
sources = append(sources, NewAccessSource(s))
sources = append(sources, NewAccessReviewSource(s))
}
var nextCursor *page.CursorKey
@@ -46,9 +46,9 @@ func NewListAccessSourcesOutput(
nextCursor = &cursorKey
}
return ListAccessSourcesOutput{
NextCursor: nextCursor,
AccessSources: sources,
return ListAccessReviewSourcesOutput{
NextCursor: nextCursor,
AccessReviewSources: sources,
}
}
@@ -67,32 +67,32 @@ func NewAccessReviewCampaign(c *coredata.AccessReviewCampaign) *AccessReviewCamp
}
}
func NewAccessEntry(e *coredata.AccessEntry) *AccessEntry {
entry := &AccessEntry{
ID: e.ID,
CampaignID: e.AccessReviewCampaignID,
AccessSourceID: e.AccessSourceID,
Email: e.Email,
FullName: e.FullName,
Role: e.Role,
JobTitle: e.JobTitle,
IsAdmin: e.IsAdmin,
Active: e.Active,
MfaStatus: e.MFAStatus,
AuthMethod: e.AuthMethod,
AccountType: e.AccountType,
LastLogin: e.LastLogin,
AccountCreatedAt: e.AccountCreatedAt,
ExternalID: e.ExternalID,
IncrementalTag: e.IncrementalTag,
Flags: e.Flags,
FlagReasons: e.FlagReasons,
Decision: e.Decision,
DecisionNote: e.DecisionNote,
DecidedBy: e.DecidedBy,
DecidedAt: e.DecidedAt,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
func NewAccessReviewEntry(e *coredata.AccessReviewEntry) *AccessReviewEntry {
entry := &AccessReviewEntry{
ID: e.ID,
CampaignID: e.AccessReviewCampaignID,
AccessReviewCampaignSourceID: e.AccessReviewCampaignSourceID,
Email: e.Email,
FullName: e.FullName,
Role: e.Role,
JobTitle: e.JobTitle,
IsAdmin: e.IsAdmin,
Active: e.Active,
MfaStatus: e.MFAStatus,
AuthMethod: e.AuthMethod,
AccountType: e.AccountType,
LastLogin: e.LastLogin,
AccountCreatedAt: e.AccountCreatedAt,
ExternalID: e.ExternalID,
IncrementalTag: e.IncrementalTag,
Flags: e.Flags,
FlagReasons: e.FlagReasons,
Decision: e.Decision,
DecisionNote: e.DecisionNote,
DecidedBy: e.DecidedBy,
DecidedAt: e.DecidedAt,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
}
return entry
@@ -120,11 +120,11 @@ func NewListAccessReviewCampaignsOutput(
}
func NewListAccessEntriesOutput(
p *page.Page[*coredata.AccessEntry, coredata.AccessEntryOrderField],
p *page.Page[*coredata.AccessReviewEntry, coredata.AccessReviewEntryOrderField],
) ListAccessEntriesOutput {
entries := make([]*AccessEntry, 0, len(p.Data))
entries := make([]*AccessReviewEntry, 0, len(p.Data))
for _, e := range p.Data {
entries = append(entries, NewAccessEntry(e))
entries = append(entries, NewAccessReviewEntry(e))
}
var nextCursor *page.CursorKey
@@ -140,7 +140,7 @@ func NewListAccessEntriesOutput(
}
}
func NewAccessEntryStatistics(s *coredata.AccessEntryStatistics) *AccessEntryStatistics {
func NewAccessReviewStatistics(s *coredata.AccessReviewStatistics) *AccessReviewStatistics {
decisionCounts := make(map[string]any, len(s.DecisionCounts))
for k, v := range s.DecisionCounts {
decisionCounts[string(k)] = v
@@ -156,7 +156,7 @@ func NewAccessEntryStatistics(s *coredata.AccessEntryStatistics) *AccessEntrySta
incrementalTagCounts[string(k)] = v
}
return &AccessEntryStatistics{
return &AccessReviewStatistics{
TotalCount: s.TotalCount,
DecisionCounts: decisionCounts,
FlagCounts: flagCounts,