Add read actions to all unprefixed scopes

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
Ludovic Vielle
2026-06-23 10:36:32 +02:00
parent 002c91ba11
commit 20b9321de3
11 changed files with 276 additions and 1 deletions

View File

@@ -33,6 +33,13 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionDriverCatalogList, ActionDriverCatalogList,
}, },
ScopeV1AccessReview: { ScopeV1AccessReview: {
ActionCampaignGet,
ActionCampaignList,
ActionEntryGet,
ActionEntryList,
ActionSourceGet,
ActionSourceList,
ActionDriverCatalogList,
ActionCampaignCreate, ActionCampaignCreate,
ActionCampaignUpdate, ActionCampaignUpdate,
ActionCampaignDelete, ActionCampaignDelete,

View File

@@ -27,6 +27,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionAgentRunList, ActionAgentRunList,
}, },
ScopeV1Agent: { ScopeV1Agent: {
ActionAgentRunGet,
ActionAgentRunList,
ActionAgentRunApprove, ActionAgentRunApprove,
}, },
} }

View File

@@ -0,0 +1,42 @@
-- Copyright (c) 2026 Probo Inc <hello@probo.com>.
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-- PERFORMANCE OF THIS SOFTWARE.
-- Narrow prb CLI OAuth2 client registration to unprefixed v1:* scopes.
UPDATE iam_oauth2_clients
SET scopes = '{
openid,
profile,
email,
offline_access,
v1:access-review,
v1:agent,
v1:asset,
v1:audit,
v1:common-third-party,
v1:compliance-page,
v1:connector,
v1:control,
v1:datum,
v1:document,
v1:iam,
v1:org,
v1:privacy,
v1:risk,
v1:slack-connection,
v1:task,
v1:third-party,
v1:webhook
}'::TEXT[],
updated_at = NOW()
WHERE id = 'AAAAAAAAAAAASwAAAAAAAAAAcHJiY2xp';

View File

@@ -37,6 +37,10 @@ func (v OAuth2Scope) String() string {
return string(v) return string(v)
} }
func (v OAuth2Scope) IsRead() bool {
return strings.HasSuffix(string(v), ":read")
}
func (v OAuth2Scope) MarshalText() ([]byte, error) { func (v OAuth2Scope) MarshalText() ([]byte, error) {
return []byte(v.String()), nil return []byte(v.String()), nil
} }

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata_test
import (
"testing"
"github.com/stretchr/testify/assert"
"go.probo.inc/probo/pkg/coredata"
)
func TestOAuth2Scope_IsRead(t *testing.T) {
t.Parallel()
tests := []struct {
scope coredata.OAuth2Scope
want bool
}{
{scope: "v1:org:read", want: true},
{scope: "v1:document:read", want: true},
{scope: "v1:org", want: false},
{scope: "v1:privacy", want: false},
{scope: "openid", want: false},
{scope: "offline_access", want: false},
}
for _, tt := range tests {
t.Run(
tt.scope.String(),
func(t *testing.T) {
t.Parallel()
assert.Equal(t, tt.want, tt.scope.IsRead())
},
)
}
}

View File

@@ -400,7 +400,7 @@ func (s *Service) upsertCIMDClient(
clientURI = &doc.ClientURI clientURI = &doc.ClientURI
} }
scopes := coredata.OAuth2Scopes(authorizationServerScopes(s.registry.RegisteredScopes())) scopes := coredata.OAuth2Scopes(authorizationServerScopes(s.registry.AllWriteScopes()))
now := time.Now() now := time.Now()

View File

@@ -49,6 +49,30 @@ var IAMOAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionOAuth2AccessTokenList, ActionOAuth2AccessTokenList,
}, },
ScopeV1IAM: { ScopeV1IAM: {
ActionOrganizationGet,
ActionOrganizationList,
ActionIdentityGet,
ActionSessionList,
ActionSessionGet,
ActionInvitationList,
ActionInvitationGet,
ActionMembershipGet,
ActionMembershipList,
ActionMembershipProfileGet,
ActionMembershipProfileList,
ActionPersonalAPIKeyGet,
ActionPersonalAPIKeyList,
ActionSAMLConfigurationGet,
ActionSAMLConfigurationList,
ActionSCIMConfigurationGet,
ActionSCIMEventList,
ActionSCIMEventGet,
ActionSCIMBridgeGet,
ActionOAuth2ConsentGet,
ActionAuditLogEntryGet,
ActionAuditLogEntryList,
ActionOAuth2AccessTokenGet,
ActionOAuth2AccessTokenList,
ActionOrganizationCreate, ActionOrganizationCreate,
ActionOrganizationUpdate, ActionOrganizationUpdate,
ActionOrganizationDelete, ActionOrganizationDelete,

View File

@@ -60,6 +60,20 @@ func (r *Registry) RegisteredScopes() []coredata.OAuth2Scope {
return sortedScopes(slices.Collect(maps.Keys(r.scopeActions))) return sortedScopes(slices.Collect(maps.Keys(r.scopeActions)))
} }
func (r *Registry) AllWriteScopes() []coredata.OAuth2Scope {
r.mu.RLock()
defer r.mu.RUnlock()
writeScopes := make([]coredata.OAuth2Scope, 0, len(r.scopeActions))
for scope := range r.scopeActions {
if !scope.IsRead() {
writeScopes = append(writeScopes, scope)
}
}
return sortedScopes(writeScopes)
}
func (r *Registry) Allows(tokenScopes coredata.OAuth2Scopes, action string) bool { func (r *Registry) Allows(tokenScopes coredata.OAuth2Scopes, action string) bool {
r.mu.RLock() r.mu.RLock()
defer r.mu.RUnlock() defer r.mu.RUnlock()

View File

@@ -105,6 +105,25 @@ func TestRegistry_ScopesForAction(t *testing.T) {
assert.Nil(t, reg.ScopesForAction("core:organization:delete")) assert.Nil(t, reg.ScopesForAction("core:organization:delete"))
} }
func TestRegistry_AllWriteScopes(t *testing.T) {
t.Parallel()
const (
scopeV1OrgRead = coredata.OAuth2Scope("v1:org:read")
scopeV1OrgWrite = coredata.OAuth2Scope("v1:org")
)
reg := oauth2scope.NewRegistry().
Register(
map[coredata.OAuth2Scope][]string{
scopeV1OrgWrite: {"core:organization:update"},
scopeV1OrgRead: {"core:organization:get"},
},
)
assert.Equal(t, []coredata.OAuth2Scope{scopeV1OrgWrite}, reg.AllWriteScopes())
}
func TestRegistry_RegisteredScopes(t *testing.T) { func TestRegistry_RegisteredScopes(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -73,6 +73,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionAssetList, ActionAssetList,
}, },
ScopeV1Asset: { ScopeV1Asset: {
ActionAssetGet,
ActionAssetList,
ActionAssetCreate, ActionAssetCreate,
ActionAssetUpdate, ActionAssetUpdate,
ActionAssetDelete, ActionAssetDelete,
@@ -88,6 +90,13 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionReportDownloadUrlGet, ActionReportDownloadUrlGet,
}, },
ScopeV1Audit: { ScopeV1Audit: {
ActionAuditGet,
ActionAuditList,
ActionFindingGet,
ActionFindingList,
ActionReportGet,
ActionReportGetReportUrl,
ActionReportDownloadUrlGet,
ActionAuditCreate, ActionAuditCreate,
ActionAuditUpdate, ActionAuditUpdate,
ActionAuditDelete, ActionAuditDelete,
@@ -122,6 +131,21 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionCustomDomainGet, ActionCustomDomainGet,
}, },
ScopeV1CompliancePage: { ScopeV1CompliancePage: {
ActionTrustCenterGet,
ActionTrustCenterGetNda,
ActionTrustCenterAccessGet,
ActionTrustCenterAccessList,
ActionTrustCenterFileGet,
ActionTrustCenterFileList,
ActionTrustCenterFileGetFileUrl,
ActionTrustCenterReferenceList,
ActionTrustCenterReferenceGetLogoUrl,
ActionTrustCenterDocumentAccessList,
ActionMailingListUpdateList,
ActionMailingListSubscriberList,
ActionComplianceFrameworkList,
ActionComplianceExternalURLList,
ActionCustomDomainGet,
ActionTrustCenterUpdate, ActionTrustCenterUpdate,
ActionTrustCenterNonDisclosureAgreementUpload, ActionTrustCenterNonDisclosureAgreementUpload,
ActionTrustCenterNonDisclosureAgreementDelete, ActionTrustCenterNonDisclosureAgreementDelete,
@@ -155,6 +179,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionConnectorGet, ActionConnectorGet,
}, },
ScopeV1Connector: { ScopeV1Connector: {
ActionConnectorList,
ActionConnectorGet,
ActionConnectorCreate, ActionConnectorCreate,
ActionConnectorDelete, ActionConnectorDelete,
ActionConnectorInitiate, ActionConnectorInitiate,
@@ -175,6 +201,19 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionApplicabilityStatementList, ActionApplicabilityStatementList,
}, },
ScopeV1Control: { ScopeV1Control: {
ActionControlGet,
ActionControlList,
ActionMeasureGet,
ActionMeasureList,
ActionFrameworkGet,
ActionFrameworkList,
ActionFrameworkExport,
ActionObligationGet,
ActionObligationList,
ActionStatementOfApplicabilityList,
ActionStatementOfApplicabilityGet,
ActionApplicabilityStatementGet,
ActionApplicabilityStatementList,
ActionControlCreate, ActionControlCreate,
ActionControlUpdate, ActionControlUpdate,
ActionControlDelete, ActionControlDelete,
@@ -216,6 +255,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionDatumList, ActionDatumList,
}, },
ScopeV1Datum: { ScopeV1Datum: {
ActionDatumGet,
ActionDatumList,
ActionDatumCreate, ActionDatumCreate,
ActionDatumUpdate, ActionDatumUpdate,
ActionDatumDelete, ActionDatumDelete,
@@ -238,6 +279,20 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionFileGet, ActionFileGet,
}, },
ScopeV1Document: { ScopeV1Document: {
ActionDocumentGet,
ActionDocumentList,
ActionDocumentVersionGet,
ActionDocumentVersionList,
ActionDocumentVersionExportPDF,
ActionDocumentVersionApprovalList,
ActionDocumentVersionExport,
ActionEmployeeDocumentGet,
ActionEmployeeDocumentList,
ActionEmployeeDocumentVersionExportPDF,
ActionDocumentVersionSignatureGet,
ActionDocumentVersionSignatureList,
ActionElectronicSignatureGet,
ActionFileGet,
ActionDocumentCreate, ActionDocumentCreate,
ActionDocumentUpdate, ActionDocumentUpdate,
ActionDocumentDelete, ActionDocumentDelete,
@@ -261,6 +316,10 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionOrganizationContextGet, ActionOrganizationContextGet,
}, },
ScopeV1Org: { ScopeV1Org: {
ActionOrganizationGet,
ActionOrganizationGetLogoUrl,
ActionOrganizationGetHorizontalLogoUrl,
ActionOrganizationContextGet,
ActionOrganizationUpdate, ActionOrganizationUpdate,
ActionOrganizationContextUpdate, ActionOrganizationContextUpdate,
}, },
@@ -288,6 +347,27 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionTrackerResourceList, ActionTrackerResourceList,
}, },
ScopeV1Privacy: { ScopeV1Privacy: {
ActionProcessingActivityList,
ActionProcessingActivityGet,
ActionDataProtectionImpactAssessmentList,
ActionDataProtectionImpactAssessmentGet,
ActionTransferImpactAssessmentList,
ActionTransferImpactAssessmentGet,
ActionRightsRequestList,
ActionRightsRequestGet,
ActionCookieBannerGet,
ActionCookieBannerList,
ActionCookieBannerVersionGet,
ActionCookieBannerVersionList,
ActionCookieCategoryGet,
ActionCookieCategoryList,
ActionCookieGet,
ActionCookieList,
ActionCookieConsentRecordList,
ActionTrackerPatternGet,
ActionTrackerPatternList,
ActionTrackerResourceGet,
ActionTrackerResourceList,
ActionProcessingActivityCreate, ActionProcessingActivityCreate,
ActionProcessingActivityUpdate, ActionProcessingActivityUpdate,
ActionProcessingActivityDelete, ActionProcessingActivityDelete,
@@ -342,6 +422,22 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionRiskAssessmentScenarioList, ActionRiskAssessmentScenarioList,
}, },
ScopeV1Risk: { ScopeV1Risk: {
ActionRiskGet,
ActionRiskList,
ActionRiskAssessmentGet,
ActionRiskAssessmentList,
ActionRiskAssessmentScopeGet,
ActionRiskAssessmentScopeList,
ActionRiskAssessmentNodeGet,
ActionRiskAssessmentNodeList,
ActionRiskAssessmentBoundaryGet,
ActionRiskAssessmentBoundaryList,
ActionRiskAssessmentProcessGet,
ActionRiskAssessmentProcessList,
ActionRiskAssessmentThreatGet,
ActionRiskAssessmentThreatList,
ActionRiskAssessmentScenarioGet,
ActionRiskAssessmentScenarioList,
ActionRiskCreate, ActionRiskCreate,
ActionRiskUpdate, ActionRiskUpdate,
ActionRiskDelete, ActionRiskDelete,
@@ -387,6 +483,9 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionEvidenceList, ActionEvidenceList,
}, },
ScopeV1Task: { ScopeV1Task: {
ActionTaskGet,
ActionTaskList,
ActionEvidenceList,
ActionTaskCreate, ActionTaskCreate,
ActionTaskUpdate, ActionTaskUpdate,
ActionTaskDelete, ActionTaskDelete,
@@ -409,6 +508,18 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionThirdPartyRiskAssessmentList, ActionThirdPartyRiskAssessmentList,
}, },
ScopeV1ThirdParty: { ScopeV1ThirdParty: {
ActionThirdPartyList,
ActionThirdPartyGet,
ActionThirdPartyRelationList,
ActionThirdPartyContactGet,
ActionThirdPartyContactList,
ActionThirdPartyServiceGet,
ActionThirdPartyServiceList,
ActionThirdPartyComplianceReportGet,
ActionThirdPartyComplianceReportList,
ActionThirdPartyBusinessAssociateAgreementGet,
ActionThirdPartyDataPrivacyAgreementGet,
ActionThirdPartyRiskAssessmentList,
ActionThirdPartyCreate, ActionThirdPartyCreate,
ActionThirdPartyUpdate, ActionThirdPartyUpdate,
ActionThirdPartyDelete, ActionThirdPartyDelete,
@@ -436,6 +547,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionWebhookSubscriptionGet, ActionWebhookSubscriptionGet,
}, },
ScopeV1Webhook: { ScopeV1Webhook: {
ActionWebhookSubscriptionList,
ActionWebhookSubscriptionGet,
ActionWebhookSubscriptionCreate, ActionWebhookSubscriptionCreate,
ActionWebhookSubscriptionUpdate, ActionWebhookSubscriptionUpdate,
ActionWebhookSubscriptionDelete, ActionWebhookSubscriptionDelete,

View File

@@ -27,6 +27,7 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionAliasGet, ActionAliasGet,
}, },
ScopeV1ResourceAlias: { ScopeV1ResourceAlias: {
ActionAliasGet,
ActionAliasSet, ActionAliasSet,
ActionAliasRemove, ActionAliasRemove,
}, },