Expose missing console capabilities on MCP
Add MCP tools and filters for mailing lists, detected trackers, compliance portal frameworks, and document/control/framework mutations that were available in the console but missing from MCP. Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
@@ -112,10 +112,11 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingProboService = errors.New("server configuration requires a valid probo.Service instance")
|
||||
ErrMissingIAMService = errors.New("server configuration requires a valid iam.Service instance")
|
||||
ErrMissingSlackService = errors.New("server configuration requires a valid slack.Service instance")
|
||||
ErrMissingITAMService = errors.New("server configuration requires a valid itam.Service instance")
|
||||
ErrMissingProboService = errors.New("server configuration requires a valid probo.Service instance")
|
||||
ErrMissingIAMService = errors.New("server configuration requires a valid iam.Service instance")
|
||||
ErrMissingSlackService = errors.New("server configuration requires a valid slack.Service instance")
|
||||
ErrMissingITAMService = errors.New("server configuration requires a valid itam.Service instance")
|
||||
ErrMissingMailmanService = errors.New("server configuration requires a valid mailman.Service instance")
|
||||
)
|
||||
|
||||
func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -159,6 +160,10 @@ func NewServer(cfg Config) (*Server, error) {
|
||||
return nil, ErrMissingITAMService
|
||||
}
|
||||
|
||||
if cfg.Mailman == nil {
|
||||
return nil, ErrMissingMailmanService
|
||||
}
|
||||
|
||||
csrf := http.NewCrossOriginProtection()
|
||||
for _, origin := range cfg.AllowedOrigins {
|
||||
if err := csrf.AddTrustedOrigin(origin); err != nil {
|
||||
@@ -246,6 +251,7 @@ func NewServer(cfg Config) (*Server, error) {
|
||||
cfg.CookieBanner,
|
||||
cfg.RiskManagement,
|
||||
cfg.ITAM,
|
||||
cfg.Mailman,
|
||||
cfg.TokenSecret,
|
||||
cfg.File,
|
||||
cfg.BaseURL,
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/itam"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/prosemirror"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
@@ -63,6 +64,7 @@ type Resolver struct {
|
||||
cookieBanner *cookiebanner.Service
|
||||
riskManagement *riskmanagement.Service
|
||||
itamSvc *itam.Service
|
||||
mailman *mailman.Service
|
||||
logger *log.Logger
|
||||
fileManager *filemanager.Service
|
||||
baseURL *baseurl.BaseURL
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/itam"
|
||||
"go.probo.inc/probo/pkg/mail"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
@@ -7645,3 +7646,479 @@ func (r *Resolver) CreateDeviceTool(ctx context.Context, req *mcp.CallToolReques
|
||||
EnrollmentURL: urls.EnrollmentURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetThirdPartyTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetThirdPartyInput) (*mcp.CallToolResult, types.GetThirdPartyOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, probo.ActionThirdPartyGet)
|
||||
if err != nil {
|
||||
return nil, types.GetThirdPartyOutput{}, err
|
||||
}
|
||||
|
||||
thirdParty, err := r.proboSvc.ThirdParties.Get(ctx, scope, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.GetThirdPartyOutput{}, fmt.Errorf("cannot get third party: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.GetThirdPartyOutput{
|
||||
ThirdParty: types.NewThirdParty(thirdParty),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteEvidenceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteEvidenceInput) (*mcp.CallToolResult, types.DeleteEvidenceOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, probo.ActionEvidenceDelete)
|
||||
if err != nil {
|
||||
return nil, types.DeleteEvidenceOutput{}, err
|
||||
}
|
||||
|
||||
err = r.proboSvc.Evidences.Delete(ctx, scope, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.DeleteEvidenceOutput{}, fmt.Errorf("cannot delete evidence: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteEvidenceOutput{
|
||||
DeletedEvidenceID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteFrameworkInput) (*mcp.CallToolResult, types.DeleteFrameworkOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, probo.ActionFrameworkDelete)
|
||||
if err != nil {
|
||||
return nil, types.DeleteFrameworkOutput{}, err
|
||||
}
|
||||
|
||||
err = r.proboSvc.Frameworks.Delete(ctx, scope, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.DeleteFrameworkOutput{}, fmt.Errorf("cannot delete framework: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteFrameworkOutput{
|
||||
DeletedFrameworkID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteAuditReportTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteAuditReportInput) (*mcp.CallToolResult, types.DeleteAuditReportOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, probo.ActionAuditReportDelete)
|
||||
if err != nil {
|
||||
return nil, types.DeleteAuditReportOutput{}, err
|
||||
}
|
||||
|
||||
audit, err := r.proboSvc.Audits.DeleteReport(ctx, scope, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.DeleteAuditReportOutput{}, fmt.Errorf("cannot delete audit report: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteAuditReportOutput{
|
||||
Audit: types.NewAudit(audit, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteControlTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteControlInput) (*mcp.CallToolResult, types.DeleteControlOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, probo.ActionControlDelete)
|
||||
if err != nil {
|
||||
return nil, types.DeleteControlOutput{}, err
|
||||
}
|
||||
|
||||
err = r.proboSvc.Controls.Delete(ctx, scope, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.DeleteControlOutput{}, fmt.Errorf("cannot delete control: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteControlOutput{
|
||||
DeletedControlID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ApproveDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ApproveDocumentVersionInput) (*mcp.CallToolResult, types.ApproveDocumentVersionOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionApprove)
|
||||
if err != nil {
|
||||
return nil, types.ApproveDocumentVersionOutput{}, err
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
signer := signerMetadataFromToolRequest(req)
|
||||
|
||||
decision, err := r.proboSvc.DocumentApprovals.Approve(
|
||||
ctx,
|
||||
scope,
|
||||
probo.ApproveDocumentVersionRequest{
|
||||
DocumentVersionID: input.DocumentVersionID,
|
||||
IdentityID: identity.ID,
|
||||
Comment: input.Comment,
|
||||
SignerFullName: identity.FullName,
|
||||
SignerEmail: identity.EmailAddress,
|
||||
SignerIPAddr: signer.IPAddr,
|
||||
SignerUA: signer.UA,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.ApproveDocumentVersionOutput{}, fmt.Errorf("cannot approve document version: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.ApproveDocumentVersionOutput{
|
||||
DocumentVersionApprovalDecision: types.NewDocumentVersionApprovalDecision(decision),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) RejectDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RejectDocumentVersionInput) (*mcp.CallToolResult, types.RejectDocumentVersionOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionReject)
|
||||
if err != nil {
|
||||
return nil, types.RejectDocumentVersionOutput{}, err
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
|
||||
decision, err := r.proboSvc.DocumentApprovals.Reject(
|
||||
ctx,
|
||||
scope,
|
||||
probo.RejectDocumentVersionRequest{
|
||||
DocumentVersionID: input.DocumentVersionID,
|
||||
IdentityID: identity.ID,
|
||||
Comment: input.Comment,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.RejectDocumentVersionOutput{}, fmt.Errorf("cannot reject document version: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.RejectDocumentVersionOutput{
|
||||
DocumentVersionApprovalDecision: types.NewDocumentVersionApprovalDecision(decision),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) SignDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.SignDocumentInput) (*mcp.CallToolResult, types.SignDocumentOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSign)
|
||||
if err != nil {
|
||||
return nil, types.SignDocumentOutput{}, err
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
signer := signerMetadataFromToolRequest(req)
|
||||
|
||||
signature, err := r.proboSvc.Documents.SignDocumentVersionByIdentity(
|
||||
ctx,
|
||||
scope,
|
||||
probo.SignDocumentVersionRequest{
|
||||
DocumentVersionID: input.DocumentVersionID,
|
||||
IdentityID: identity.ID,
|
||||
SignerFullName: identity.FullName,
|
||||
SignerEmail: identity.EmailAddress,
|
||||
SignerIPAddr: signer.IPAddr,
|
||||
SignerUA: signer.UA,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.SignDocumentOutput{}, fmt.Errorf("cannot sign document: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.SignDocumentOutput{
|
||||
DocumentVersionSignature: types.NewDocumentVersionSignature(signature),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetMailingListTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetMailingListInput) (*mcp.CallToolResult, types.GetMailingListOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.CompliancePortalID, management.ActionMailingListSubscriberList)
|
||||
if err != nil {
|
||||
return nil, types.GetMailingListOutput{}, err
|
||||
}
|
||||
|
||||
mailingList, err := r.management.GetMailingList(ctx, scope, input.CompliancePortalID)
|
||||
if err != nil {
|
||||
return nil, types.GetMailingListOutput{}, fmt.Errorf("cannot get mailing list: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.GetMailingListOutput{MailingList: types.NewMailingList(mailingList)}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) UpdateMailingListTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateMailingListInput) (*mcp.CallToolResult, types.UpdateMailingListOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.ID, management.ActionMailingListUpdate); err != nil {
|
||||
return nil, types.UpdateMailingListOutput{}, err
|
||||
}
|
||||
|
||||
mailingList, err := r.mailman.UpdateMailingList(ctx, input.ID, input.ReplyTo)
|
||||
if err != nil {
|
||||
return nil, types.UpdateMailingListOutput{}, fmt.Errorf("cannot update mailing list: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UpdateMailingListOutput{MailingList: types.NewMailingList(mailingList)}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListMailingListSubscribersTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMailingListSubscribersInput) (*mcp.CallToolResult, types.ListMailingListSubscribersOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.MailingListID, management.ActionMailingListSubscriberList); err != nil {
|
||||
return nil, types.ListMailingListSubscribersOutput{}, err
|
||||
}
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.MailingListSubscriberOrderField]{
|
||||
Field: coredata.MailingListSubscriberOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if input.OrderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.MailingListSubscriberOrderField]{
|
||||
Field: input.OrderBy.Field,
|
||||
Direction: input.OrderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
|
||||
|
||||
p, err := r.mailman.ListSubscribers(ctx, input.MailingListID, cursor)
|
||||
if err != nil {
|
||||
return nil, types.ListMailingListSubscribersOutput{}, fmt.Errorf("cannot list mailing list subscribers: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.NewListMailingListSubscribersOutput(p), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) AddMailingListSubscriberTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddMailingListSubscriberInput) (*mcp.CallToolResult, types.AddMailingListSubscriberOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.MailingListID, management.ActionMailingListSubscriberCreate); err != nil {
|
||||
return nil, types.AddMailingListSubscriberOutput{}, err
|
||||
}
|
||||
|
||||
subscriber, err := r.mailman.CreateSubscriber(
|
||||
ctx,
|
||||
&mailman.CreateSubscriberRequest{
|
||||
MailingListID: input.MailingListID,
|
||||
Email: input.Email,
|
||||
FullName: input.FullName,
|
||||
Confirmed: input.Confirmed != nil && *input.Confirmed,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.AddMailingListSubscriberOutput{}, fmt.Errorf("cannot add mailing list subscriber: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.AddMailingListSubscriberOutput{
|
||||
MailingListSubscriber: types.NewMailingListSubscriber(subscriber),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteMailingListSubscriberTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteMailingListSubscriberInput) (*mcp.CallToolResult, types.DeleteMailingListSubscriberOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.ID, management.ActionMailingListSubscriberDelete); err != nil {
|
||||
return nil, types.DeleteMailingListSubscriberOutput{}, err
|
||||
}
|
||||
|
||||
if err := r.mailman.DeleteSubscriber(ctx, input.ID); err != nil {
|
||||
return nil, types.DeleteMailingListSubscriberOutput{}, fmt.Errorf("cannot delete mailing list subscriber: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteMailingListSubscriberOutput{
|
||||
DeletedMailingListSubscriberID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListMailingListUpdatesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListMailingListUpdatesInput) (*mcp.CallToolResult, types.ListMailingListUpdatesOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.MailingListID, management.ActionMailingListUpdateList); err != nil {
|
||||
return nil, types.ListMailingListUpdatesOutput{}, err
|
||||
}
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.MailingListUpdateOrderField]{
|
||||
Field: coredata.MailingListUpdateOrderFieldUpdatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if input.OrderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.MailingListUpdateOrderField]{
|
||||
Field: input.OrderBy.Field,
|
||||
Direction: input.OrderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
|
||||
|
||||
p, err := r.mailman.ListMailingListUpdates(ctx, input.MailingListID, cursor)
|
||||
if err != nil {
|
||||
return nil, types.ListMailingListUpdatesOutput{}, fmt.Errorf("cannot list mailing list updates: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.NewListMailingListUpdatesOutput(p), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) AddMailingListUpdateTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddMailingListUpdateInput) (*mcp.CallToolResult, types.AddMailingListUpdateOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.MailingListID, management.ActionMailingListUpdateCreate); err != nil {
|
||||
return nil, types.AddMailingListUpdateOutput{}, err
|
||||
}
|
||||
|
||||
update, err := r.mailman.CreateMailingListUpdate(
|
||||
ctx,
|
||||
&mailman.CreateMailingListUpdateRequest{
|
||||
MailingListID: input.MailingListID,
|
||||
Title: input.Title,
|
||||
Body: input.Body,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.AddMailingListUpdateOutput{}, fmt.Errorf("cannot add mailing list update: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.AddMailingListUpdateOutput{
|
||||
MailingListUpdate: types.NewMailingListUpdate(update),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) UpdateMailingListUpdateTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateMailingListUpdateInput) (*mcp.CallToolResult, types.UpdateMailingListUpdateOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.ID, management.ActionMailingListUpdateUpdate); err != nil {
|
||||
return nil, types.UpdateMailingListUpdateOutput{}, err
|
||||
}
|
||||
|
||||
update, err := r.mailman.UpdateMailingListUpdate(
|
||||
ctx,
|
||||
&mailman.UpdateMailingListUpdateRequest{
|
||||
ID: input.ID,
|
||||
Title: input.Title,
|
||||
Body: input.Body,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.UpdateMailingListUpdateOutput{}, fmt.Errorf("cannot update mailing list update: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UpdateMailingListUpdateOutput{
|
||||
MailingListUpdate: types.NewMailingListUpdate(update),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) SendMailingListUpdateTool(ctx context.Context, req *mcp.CallToolRequest, input *types.SendMailingListUpdateInput) (*mcp.CallToolResult, types.SendMailingListUpdateOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.ID, management.ActionMailingListUpdateUpdate); err != nil {
|
||||
return nil, types.SendMailingListUpdateOutput{}, err
|
||||
}
|
||||
|
||||
update, err := r.mailman.SendMailingListUpdate(ctx, input.ID)
|
||||
if err != nil {
|
||||
return nil, types.SendMailingListUpdateOutput{}, fmt.Errorf("cannot send mailing list update: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.SendMailingListUpdateOutput{
|
||||
MailingListUpdate: types.NewMailingListUpdate(update),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteMailingListUpdateTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteMailingListUpdateInput) (*mcp.CallToolResult, types.DeleteMailingListUpdateOutput, error) {
|
||||
if _, err := r.Authorize(ctx, input.ID, management.ActionMailingListUpdateDelete); err != nil {
|
||||
return nil, types.DeleteMailingListUpdateOutput{}, err
|
||||
}
|
||||
|
||||
if err := r.mailman.DeleteMailingListUpdate(ctx, input.ID); err != nil {
|
||||
return nil, types.DeleteMailingListUpdateOutput{}, fmt.Errorf("cannot delete mailing list update: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteMailingListUpdateOutput{
|
||||
DeletedMailingListUpdateID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListDetectedTrackersTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListDetectedTrackersInput) (*mcp.CallToolResult, types.ListDetectedTrackersOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternGet)
|
||||
if err != nil {
|
||||
return nil, types.ListDetectedTrackersOutput{}, err
|
||||
}
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.DetectedTrackerOrderField]{
|
||||
Field: coredata.DetectedTrackerOrderFieldLastDetectedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if input.OrderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.DetectedTrackerOrderField]{
|
||||
Field: input.OrderBy.Field,
|
||||
Direction: input.OrderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
|
||||
|
||||
trackers, err := r.cookieBanner.ListDetectedTrackersForPattern(ctx, scope, input.TrackerPatternID, cursor)
|
||||
if err != nil {
|
||||
return nil, types.ListDetectedTrackersOutput{}, fmt.Errorf("cannot list detected trackers: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.NewListDetectedTrackersOutput(page.NewPage(trackers, cursor)), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListCompliancePortalFrameworksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCompliancePortalFrameworksInput) (*mcp.CallToolResult, types.ListCompliancePortalFrameworksOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.CompliancePortalID, management.ActionComplianceFrameworkList)
|
||||
if err != nil {
|
||||
return nil, types.ListCompliancePortalFrameworksOutput{}, err
|
||||
}
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.ComplianceFrameworkOrderField]{
|
||||
Field: coredata.ComplianceFrameworkOrderFieldRank,
|
||||
Direction: page.OrderDirectionAsc,
|
||||
}
|
||||
if input.OrderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.ComplianceFrameworkOrderField]{
|
||||
Field: input.OrderBy.Field,
|
||||
Direction: input.OrderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
|
||||
|
||||
p, err := r.management.ListFrameworksWithHidden(ctx, scope, input.CompliancePortalID, cursor)
|
||||
if err != nil {
|
||||
return nil, types.ListCompliancePortalFrameworksOutput{}, fmt.Errorf("cannot list compliance portal frameworks: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.NewListCompliancePortalFrameworksOutput(p), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) AddCompliancePortalFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddCompliancePortalFrameworkInput) (*mcp.CallToolResult, types.AddCompliancePortalFrameworkOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.CompliancePortalID, management.ActionComplianceFrameworkCreate)
|
||||
if err != nil {
|
||||
return nil, types.AddCompliancePortalFrameworkOutput{}, err
|
||||
}
|
||||
|
||||
framework, err := r.management.CreateFramework(
|
||||
ctx,
|
||||
scope,
|
||||
&management.CreateFrameworkRequest{
|
||||
CompliancePortalID: input.CompliancePortalID,
|
||||
FrameworkID: input.FrameworkID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.AddCompliancePortalFrameworkOutput{}, fmt.Errorf("cannot add compliance portal framework: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.AddCompliancePortalFrameworkOutput{
|
||||
CompliancePortalFramework: types.NewCompliancePortalFramework(framework),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) UpdateCompliancePortalFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCompliancePortalFrameworkInput) (*mcp.CallToolResult, types.UpdateCompliancePortalFrameworkOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, management.ActionComplianceFrameworkUpdateRank)
|
||||
if err != nil {
|
||||
return nil, types.UpdateCompliancePortalFrameworkOutput{}, err
|
||||
}
|
||||
|
||||
framework, err := r.management.UpdateFramework(
|
||||
ctx,
|
||||
scope,
|
||||
&management.UpdateFrameworkRequest{
|
||||
ID: input.ID,
|
||||
Rank: input.Rank,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.UpdateCompliancePortalFrameworkOutput{}, fmt.Errorf("cannot update compliance portal framework: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UpdateCompliancePortalFrameworkOutput{
|
||||
CompliancePortalFramework: types.NewCompliancePortalFramework(framework),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteCompliancePortalFrameworkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCompliancePortalFrameworkInput) (*mcp.CallToolResult, types.DeleteCompliancePortalFrameworkOutput, error) {
|
||||
scope, err := r.Authorize(ctx, input.ID, management.ActionComplianceFrameworkDelete)
|
||||
if err != nil {
|
||||
return nil, types.DeleteCompliancePortalFrameworkOutput{}, err
|
||||
}
|
||||
|
||||
err = r.management.DeleteFramework(
|
||||
ctx,
|
||||
scope,
|
||||
&management.DeleteFrameworkRequest{ID: input.ID},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, types.DeleteCompliancePortalFrameworkOutput{}, fmt.Errorf("cannot delete compliance portal framework: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.DeleteCompliancePortalFrameworkOutput{
|
||||
DeletedCompliancePortalFrameworkID: input.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
46
pkg/server/api/mcp/v1/signer_metadata.go
Normal file
46
pkg/server/api/mcp/v1/signer_metadata.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package mcp_v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"go.probo.inc/probo/pkg/server/api/clientip"
|
||||
)
|
||||
|
||||
type signerMetadata struct {
|
||||
IPAddr string
|
||||
UA string
|
||||
}
|
||||
|
||||
func signerMetadataFromToolRequest(req *mcp.CallToolRequest) signerMetadata {
|
||||
if req == nil || req.Extra == nil || req.Extra.Header == nil {
|
||||
return signerMetadata{}
|
||||
}
|
||||
|
||||
httpReq := &http.Request{Header: req.Extra.Header}
|
||||
|
||||
return signerMetadata{
|
||||
IPAddr: clientip.Extract(httpReq),
|
||||
UA: req.Extra.Header.Get("User-Agent"),
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
60
pkg/server/api/mcp/v1/types/compliance_portal_framework.go
Normal file
60
pkg/server/api/mcp/v1/types/compliance_portal_framework.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewCompliancePortalFramework(cf *coredata.ComplianceFramework) *CompliancePortalFramework {
|
||||
return &CompliancePortalFramework{
|
||||
ID: cf.ID,
|
||||
OrganizationID: cf.OrganizationID,
|
||||
CompliancePortalID: cf.CompliancePortalID,
|
||||
FrameworkID: cf.FrameworkID,
|
||||
Rank: cf.Rank,
|
||||
Visibility: cf.Visibility,
|
||||
CreatedAt: cf.CreatedAt,
|
||||
UpdatedAt: cf.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListCompliancePortalFrameworksOutput(
|
||||
p *page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField],
|
||||
) ListCompliancePortalFrameworksOutput {
|
||||
frameworks := make([]*CompliancePortalFramework, 0, len(p.Data))
|
||||
for _, cf := range p.Data {
|
||||
frameworks = append(frameworks, NewCompliancePortalFramework(cf))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
|
||||
if len(p.Data) > 0 {
|
||||
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
|
||||
nextCursor = &cursorKey
|
||||
}
|
||||
|
||||
return ListCompliancePortalFrameworksOutput{
|
||||
NextCursor: nextCursor,
|
||||
CompliancePortalFrameworks: frameworks,
|
||||
}
|
||||
}
|
||||
66
pkg/server/api/mcp/v1/types/detected_tracker.go
Normal file
66
pkg/server/api/mcp/v1/types/detected_tracker.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewDetectedTracker(dt *coredata.DetectedTracker) *DetectedTracker {
|
||||
var source *DetectedTrackerSource
|
||||
|
||||
if dt.Source != nil {
|
||||
s := DetectedTrackerSource(string(*dt.Source))
|
||||
source = &s
|
||||
}
|
||||
|
||||
return &DetectedTracker{
|
||||
ID: dt.ID,
|
||||
Identifier: dt.Identifier,
|
||||
InitiatorURL: dt.InitiatorURL,
|
||||
MaxAgeSeconds: dt.MaxAgeSeconds,
|
||||
Source: source,
|
||||
LastDetectedAt: dt.LastDetectedAt,
|
||||
CreatedAt: dt.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListDetectedTrackersOutput(
|
||||
p *page.Page[*coredata.DetectedTracker, coredata.DetectedTrackerOrderField],
|
||||
) ListDetectedTrackersOutput {
|
||||
trackers := make([]*DetectedTracker, 0, len(p.Data))
|
||||
for _, dt := range p.Data {
|
||||
trackers = append(trackers, NewDetectedTracker(dt))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
|
||||
if len(p.Data) > 0 {
|
||||
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
|
||||
nextCursor = &cursorKey
|
||||
}
|
||||
|
||||
return ListDetectedTrackersOutput{
|
||||
NextCursor: nextCursor,
|
||||
DetectedTrackers: trackers,
|
||||
}
|
||||
}
|
||||
108
pkg/server/api/mcp/v1/types/mailing_list.go
Normal file
108
pkg/server/api/mcp/v1/types/mailing_list.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewMailingList(ml *coredata.MailingList) *MailingList {
|
||||
if ml == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &MailingList{
|
||||
ID: ml.ID,
|
||||
OrganizationID: ml.OrganizationID,
|
||||
ReplyTo: ml.ReplyTo,
|
||||
CreatedAt: ml.CreatedAt,
|
||||
UpdatedAt: ml.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMailingListSubscriber(s *coredata.MailingListSubscriber) *MailingListSubscriber {
|
||||
return &MailingListSubscriber{
|
||||
ID: s.ID,
|
||||
OrganizationID: s.OrganizationID,
|
||||
MailingListID: s.MailingListID,
|
||||
FullName: s.FullName,
|
||||
Email: s.Email,
|
||||
Status: s.Status,
|
||||
CreatedAt: s.CreatedAt,
|
||||
UpdatedAt: s.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListMailingListSubscribersOutput(
|
||||
p *page.Page[*coredata.MailingListSubscriber, coredata.MailingListSubscriberOrderField],
|
||||
) ListMailingListSubscribersOutput {
|
||||
subscribers := make([]*MailingListSubscriber, 0, len(p.Data))
|
||||
for _, s := range p.Data {
|
||||
subscribers = append(subscribers, NewMailingListSubscriber(s))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
|
||||
if len(p.Data) > 0 {
|
||||
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
|
||||
nextCursor = &cursorKey
|
||||
}
|
||||
|
||||
return ListMailingListSubscribersOutput{
|
||||
NextCursor: nextCursor,
|
||||
MailingListSubscribers: subscribers,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMailingListUpdate(u *coredata.MailingListUpdate) *MailingListUpdate {
|
||||
return &MailingListUpdate{
|
||||
ID: u.ID,
|
||||
OrganizationID: u.OrganizationID,
|
||||
MailingListID: u.MailingListID,
|
||||
Title: u.Title,
|
||||
Body: u.Body,
|
||||
Status: u.Status,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListMailingListUpdatesOutput(
|
||||
p *page.Page[*coredata.MailingListUpdate, coredata.MailingListUpdateOrderField],
|
||||
) ListMailingListUpdatesOutput {
|
||||
updates := make([]*MailingListUpdate, 0, len(p.Data))
|
||||
for _, u := range p.Data {
|
||||
updates = append(updates, NewMailingListUpdate(u))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
|
||||
if len(p.Data) > 0 {
|
||||
cursorKey := p.Data[len(p.Data)-1].CursorKey(p.Cursor.OrderBy.Field)
|
||||
nextCursor = &cursorKey
|
||||
}
|
||||
|
||||
return ListMailingListUpdatesOutput{
|
||||
NextCursor: nextCursor,
|
||||
MailingListUpdates: updates,
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/itam"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
"go.probo.inc/probo/pkg/riskmanagement"
|
||||
@@ -56,6 +57,7 @@ func NewMux(
|
||||
cookieBannerSvc *cookiebanner.Service,
|
||||
riskManagementSvc *riskmanagement.Service,
|
||||
itamSvc *itam.Service,
|
||||
mailmanSvc *mailman.Service,
|
||||
tokenSecret string,
|
||||
fileManagerSvc *filemanager.Service,
|
||||
baseURL *baseurl.BaseURL,
|
||||
@@ -75,6 +77,7 @@ func NewMux(
|
||||
cookieBanner: cookieBannerSvc,
|
||||
riskManagement: riskManagementSvc,
|
||||
itamSvc: itamSvc,
|
||||
mailman: mailmanSvc,
|
||||
logger: logger,
|
||||
fileManager: fileManagerSvc,
|
||||
baseURL: baseURL,
|
||||
|
||||
Reference in New Issue
Block a user