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,
},
ScopeV1AccessReview: {
ActionCampaignGet,
ActionCampaignList,
ActionEntryGet,
ActionEntryList,
ActionSourceGet,
ActionSourceList,
ActionDriverCatalogList,
ActionCampaignCreate,
ActionCampaignUpdate,
ActionCampaignDelete,

View File

@@ -27,6 +27,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionAgentRunList,
},
ScopeV1Agent: {
ActionAgentRunGet,
ActionAgentRunList,
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)
}
func (v OAuth2Scope) IsRead() bool {
return strings.HasSuffix(string(v), ":read")
}
func (v OAuth2Scope) MarshalText() ([]byte, error) {
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
}
scopes := coredata.OAuth2Scopes(authorizationServerScopes(s.registry.RegisteredScopes()))
scopes := coredata.OAuth2Scopes(authorizationServerScopes(s.registry.AllWriteScopes()))
now := time.Now()

View File

@@ -49,6 +49,30 @@ var IAMOAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
ActionOAuth2AccessTokenList,
},
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,
ActionOrganizationUpdate,
ActionOrganizationDelete,

View File

@@ -60,6 +60,20 @@ func (r *Registry) RegisteredScopes() []coredata.OAuth2Scope {
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 {
r.mu.RLock()
defer r.mu.RUnlock()

View File

@@ -105,6 +105,25 @@ func TestRegistry_ScopesForAction(t *testing.T) {
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) {
t.Parallel()

View File

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

View File

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