diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index ff84f13ac..b12016cfd 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -98,9 +98,9 @@ type GCP struct { ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` - ServiceAccounts []string `json:"serviceAccounts"` - ProjectIDs []string `json:"projectIDs"` - OrganizationID string `json:"organizationID"` + ServiceAccounts []string `json:"serviceAccounts,omitempty"` + ProjectIDs []string `json:"projectIDs,omitempty"` + OrganizationID string `json:"organizationID,omitempty"` DisableCustomSANs bool `json:"disableCustomSANs"` DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` DisableSSHCAUser *bool `json:"disableSSHCAUser,omitempty"` @@ -245,13 +245,17 @@ func (p *GCP) Init(config Config) (err error) { return } + // Initialize the project validator + if p.projectValidator, err = gcp.NewOrganizationValidator(p.ProjectIDs, p.OrganizationID); err != nil { + return + } + config.Audiences = config.Audiences.WithFragment(p.GetIDForToken()) if p.ctl, err = NewController(p, p.Claims, config, p.Options); err != nil { return } - p.projectValidator = gcp.NewOrganizationValidator(p.ProjectIDs, p.OrganizationID) return } diff --git a/authority/provisioner/gcp/projectvalidator.go b/authority/provisioner/gcp/projectvalidator.go index 2b92c5b6a..a7dc5cf11 100644 --- a/authority/provisioner/gcp/projectvalidator.go +++ b/authority/provisioner/gcp/projectvalidator.go @@ -29,14 +29,27 @@ func (p *ProjectValidator) ValidateProject(_ context.Context, projectID string) type OrganizationValidator struct { *ProjectValidator - OrganizationID string + OrganizationID string + projectsService *cloudresourcemanager.ProjectsService } -func NewOrganizationValidator(projectIDs []string, organizationID string) *OrganizationValidator { - return &OrganizationValidator{ - &ProjectValidator{projectIDs}, - organizationID, +func NewOrganizationValidator(projectIDs []string, organizationID string) (*OrganizationValidator, error) { + var svc *cloudresourcemanager.ProjectsService + + if organizationID != "" { + crm, err := cloudresourcemanager.NewService(context.Background()) + if err != nil { + return nil, err + } + + svc = crm.Projects } + + return &OrganizationValidator{ + ProjectValidator: &ProjectValidator{projectIDs}, + OrganizationID: organizationID, + projectsService: svc, + }, nil } func (p *OrganizationValidator) ValidateProject(ctx context.Context, projectID string) error { @@ -48,12 +61,7 @@ func (p *OrganizationValidator) ValidateProject(ctx context.Context, projectID s return nil } - crm, err := cloudresourcemanager.NewService(ctx) - if err != nil { - return err - } - - ancestry, err := crm.Projects. + ancestry, err := p.projectsService. GetAncestry(projectID, &cloudresourcemanager.GetAncestryRequest{}). Context(ctx). Do() diff --git a/authority/provisioner/gcp/projectvalidator_test.go b/authority/provisioner/gcp/projectvalidator_test.go index 1b68813c2..52baf3858 100644 --- a/authority/provisioner/gcp/projectvalidator_test.go +++ b/authority/provisioner/gcp/projectvalidator_test.go @@ -39,21 +39,62 @@ func TestProjectValidator_ValidateProject(t *testing.T) { } func TestNewOrganizationValidator(t *testing.T) { - got := NewOrganizationValidator([]string{"project-1", "project-2"}, "organization") - assert.Equal(t, &OrganizationValidator{ - ProjectValidator: &ProjectValidator{ProjectIDs: []string{"project-1", "project-2"}}, - OrganizationID: "organization", - }, got) + ctx := context.Background() + _, err := cloudresourcemanager.NewService(ctx) + skip := (err != nil) + + type args struct { + projectIDs []string + organizationID string + } + tests := []struct { + name string + skip bool + args args + want *OrganizationValidator + assertion assert.ErrorAssertionFunc + }{ + {"ok projects", false, args{[]string{"project-1", "project-2"}, ""}, &OrganizationValidator{ + ProjectValidator: &ProjectValidator{[]string{"project-1", "project-2"}}, + }, assert.NoError}, + {"ok organization", skip, args{[]string{}, "organization"}, &OrganizationValidator{ + ProjectValidator: &ProjectValidator{[]string{}}, + OrganizationID: "organization", + projectsService: &cloudresourcemanager.ProjectsService{}, + }, assert.NoError}, + {"ok projects organization", skip, args{[]string{"project-1"}, "organization"}, &OrganizationValidator{ + ProjectValidator: &ProjectValidator{[]string{"project-1"}}, + OrganizationID: "organization", + projectsService: &cloudresourcemanager.ProjectsService{}, + }, assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.skip { + t.SkipNow() + return + } + got, err := NewOrganizationValidator(tt.args.projectIDs, tt.args.organizationID) + tt.assertion(t, err) + assert.EqualExportedValues(t, tt.want, got) + }) + } } func TestOrganizationValidator_ValidateProject(t *testing.T) { ctx := context.Background() - _, err := cloudresourcemanager.NewService(ctx) + svc, err := cloudresourcemanager.NewService(ctx) skip := (err != nil) + var projectsService *cloudresourcemanager.ProjectsService + if !skip { + projectsService = svc.Projects + } + type fields struct { ProjectValidator *ProjectValidator OrganizationID string + projectsService *cloudresourcemanager.ProjectsService } type args struct { ctx context.Context @@ -66,15 +107,16 @@ func TestOrganizationValidator_ValidateProject(t *testing.T) { args args assertion assert.ErrorAssertionFunc }{ - {"ok projects", false, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, ""}, args{ctx, "allowed"}, assert.NoError}, - {"fail projects", false, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, "organization"}, args{ctx, "not-allowed"}, assert.Error}, - {"fail organization", skip, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, "fake-organization"}, args{ctx, "allowed"}, assert.Error}, + {"ok projects", false, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, "", projectsService}, args{ctx, "allowed"}, assert.NoError}, + {"fail projects", false, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, "organization", projectsService}, args{ctx, "not-allowed"}, assert.Error}, + {"fail organization", skip, fields{&ProjectValidator{ProjectIDs: []string{"allowed"}}, "fake-organization", projectsService}, args{ctx, "allowed"}, assert.Error}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := &OrganizationValidator{ ProjectValidator: tt.fields.ProjectValidator, OrganizationID: tt.fields.OrganizationID, + projectsService: tt.fields.projectsService, } if tt.skip { t.SkipNow() diff --git a/authority/provisioners.go b/authority/provisioners.go index 43a14da0e..d01048564 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -955,6 +955,7 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Name: p.Name, ServiceAccounts: cfg.ServiceAccounts, ProjectIDs: cfg.ProjectIds, + OrganizationID: cfg.OrganizationId, DisableCustomSANs: cfg.DisableCustomSans, DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, DisableSSHCAUser: cfg.DisableSshCaUser, @@ -1097,6 +1098,7 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro GCP: &linkedca.GCPProvisioner{ ServiceAccounts: p.ServiceAccounts, ProjectIds: p.ProjectIDs, + OrganizationId: p.OrganizationID, DisableCustomSans: p.DisableCustomSANs, DisableTrustOnFirstUse: p.DisableTrustOnFirstUse, DisableSshCaUser: p.DisableSSHCAUser, diff --git a/go.mod b/go.mod index fb7c11980..06a9e36dd 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/cli-utils v0.12.1 github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca - github.com/smallstep/linkedca v0.23.0 + github.com/smallstep/linkedca v0.24.0 github.com/smallstep/nosql v0.7.0 github.com/smallstep/pkcs7 v0.2.1 github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101 @@ -42,7 +42,7 @@ require ( golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 google.golang.org/api v0.249.0 - google.golang.org/grpc v1.75.0 + google.golang.org/grpc v1.75.1 google.golang.org/protobuf v1.36.9 ) @@ -63,7 +63,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/aws/aws-sdk-go v1.49.22 // indirect github.com/aws/aws-sdk-go-v2 v1.38.0 // indirect @@ -159,13 +159,13 @@ require ( go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/mod v0.27.0 // indirect + golang.org/x/mod v0.28.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect diff --git a/go.sum b/go.sum index 84edcd9a5..f6e44d9d9 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -317,8 +317,8 @@ github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -342,8 +342,8 @@ github.com/smallstep/cli-utils v0.12.1 h1:D9QvfbFqiKq3snGZ2xDcXEFrdFJ1mQfPHZMq/l github.com/smallstep/cli-utils v0.12.1/go.mod h1:skV2Neg8qjiKPu2fphM89H9bIxNpKiiRTnX9Q6Lc+20= github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4= github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4= -github.com/smallstep/linkedca v0.23.0 h1:5W/7EudlK1HcCIdZM68dJlZ7orqCCCyv6bm2l/0JmLU= -github.com/smallstep/linkedca v0.23.0/go.mod h1:7cyRM9soAYySg9ag65QwytcgGOM+4gOlkJ/YA58A9E8= +github.com/smallstep/linkedca v0.24.0 h1:7nQuHLrI7XQVbZUgvNsUiW35mskyK1itsZyboZxor3E= +github.com/smallstep/linkedca v0.24.0/go.mod h1:7VovSkUuLpO4sJPUxp25aEo9+3XIcgEEMoj2noEQFcI= github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE= github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU= github.com/smallstep/pkcs7 v0.0.0-20240911091500-b1cae6277023/go.mod h1:CM5KrX7rxWgwDdMj9yef/pJB2OPgy/56z4IEx2UIbpc= @@ -430,8 +430,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -517,8 +517,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= @@ -530,8 +530,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1: google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=