Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions pkg/feeds/feed_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (

type FeedResource struct {
AccessKey string `json:"AccessKey,omitempty"`
AzureContainerRegistryOidcAuthentication *AzureContainerRegistryOidcAuthentication `json:"AzureContainerRegistryOidcAuthentication,omitempty"`
ElasticContainerRegistryOidcAuthentication *AwsElasticContainerRegistryOidcAuthentication `json:"OidcAuthentication,omitempty"`
OidcAuthentication *OidcAuthentication `json:"OidcAuthentication,omitempty"`
APIVersion string `json:"ApiVersion,omitempty"`
DeletePackagesAssociatedWithReleases bool `json:"DeletePackagesAssociatedWithReleases"`
DeleteUnreleasedPackagesAfterDays int `json:"DeleteUnreleasedPackagesAfterDays"`
Expand All @@ -19,7 +18,6 @@ type FeedResource struct {
EnhancedMode bool `json:"EnhancedMode"`
FeedType FeedType `json:"FeedType" validate:"required,notblank"`
FeedURI string `json:"FeedUri,omitempty"`
GoogleContainerRegistryOidcAuthentication *GoogleContainerRegistryOidcAuthentication `json:"GoogleContainerRegistryOidcAuthentication,omitempty"`
IsBuiltInRepoSyncEnabled bool `json:"IsBuiltInRepoSyncEnabled"`
Name string `json:"Name" validate:"required,notblank"`
Password *core.SensitiveValue `json:"Password,omitempty"`
Expand Down
58 changes: 38 additions & 20 deletions pkg/feeds/feed_utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@ func ToFeed(feedResource *FeedResource) (IFeed, error) {

switch feedResource.GetFeedType() {
case FeedTypeAwsElasticContainerRegistry:
awsElasticContainerRegistry, err := NewAwsElasticContainerRegistry(feedResource.GetName(), feedResource.AccessKey, feedResource.SecretKey, feedResource.Region, feedResource.ElasticContainerRegistryOidcAuthentication)
var awsOidc *AwsElasticContainerRegistryOidcAuthentication
if feedResource.OidcAuthentication != nil {
if aws, ok := feedResource.OidcAuthentication.GetAWS(); ok {
awsOidc = aws
}
}
awsElasticContainerRegistry, err := NewAwsElasticContainerRegistry(feedResource.GetName(), feedResource.AccessKey, feedResource.SecretKey, feedResource.Region, awsOidc)
if err != nil {
return nil, err
}
feed = awsElasticContainerRegistry
case FeedTypeAzureContainerRegistry:
azureContainerRegistry, err := NewAzureContainerRegistry(feedResource.GetName(), feedResource.GetUsername(), feedResource.GetPassword(), feedResource.AzureContainerRegistryOidcAuthentication)
var azureOidc *AzureContainerRegistryOidcAuthentication
if feedResource.OidcAuthentication != nil {
if azure, ok := feedResource.OidcAuthentication.GetAzure(); ok {
azureOidc = azure
}
}
azureContainerRegistry, err := NewAzureContainerRegistry(feedResource.GetName(), feedResource.GetUsername(), feedResource.GetPassword(), azureOidc)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -65,7 +77,13 @@ func ToFeed(feedResource *FeedResource) (IFeed, error) {
gitHubRepositoryFeed.FeedURI = feedResource.FeedURI
feed = gitHubRepositoryFeed
case FeedTypeGoogleContainerRegistry:
googleContainerRegistry, err := NewGoogleContainerRegistry(feedResource.GetName(), feedResource.GetUsername(), feedResource.GetPassword(), feedResource.GoogleContainerRegistryOidcAuthentication)
var googleOidc *GoogleContainerRegistryOidcAuthentication
if feedResource.OidcAuthentication != nil {
if google, ok := feedResource.OidcAuthentication.GetGoogle(); ok {
googleOidc = google
}
}
googleContainerRegistry, err := NewGoogleContainerRegistry(feedResource.GetName(), feedResource.GetUsername(), feedResource.GetPassword(), googleOidc)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -169,25 +187,25 @@ func ToFeedResource(feed IFeed) (*FeedResource, error) {
feedResource.Region = awsElasticContainerRegistry.Region
feedResource.SecretKey = awsElasticContainerRegistry.SecretKey
if awsElasticContainerRegistry.OidcAuthentication != nil {
feedResource.ElasticContainerRegistryOidcAuthentication = &AwsElasticContainerRegistryOidcAuthentication{
SessionDuration: awsElasticContainerRegistry.OidcAuthentication.SessionDuration,
Audience: awsElasticContainerRegistry.OidcAuthentication.Audience,
SubjectKeys: awsElasticContainerRegistry.OidcAuthentication.SubjectKeys,
RoleArn: awsElasticContainerRegistry.OidcAuthentication.RoleArn,
}
feedResource.OidcAuthentication = NewAwsOidcAuthentication(
awsElasticContainerRegistry.OidcAuthentication.SessionDuration,
awsElasticContainerRegistry.OidcAuthentication.Audience,
awsElasticContainerRegistry.OidcAuthentication.RoleArn,
awsElasticContainerRegistry.OidcAuthentication.SubjectKeys,
)
}
case FeedTypeAzureContainerRegistry:
azureContainerRegistry := feed.(*AzureContainerRegistry)
feedResource.APIVersion = azureContainerRegistry.APIVersion
feedResource.FeedURI = azureContainerRegistry.FeedURI
feedResource.RegistryPath = azureContainerRegistry.RegistryPath
if azureContainerRegistry.OidcAuthentication != nil {
feedResource.AzureContainerRegistryOidcAuthentication = &AzureContainerRegistryOidcAuthentication{
ClientId: azureContainerRegistry.OidcAuthentication.ClientId,
TenantId: azureContainerRegistry.OidcAuthentication.TenantId,
Audience: azureContainerRegistry.OidcAuthentication.Audience,
SubjectKeys: azureContainerRegistry.OidcAuthentication.SubjectKeys,
}
feedResource.OidcAuthentication = NewAzureOidcAuthentication(
azureContainerRegistry.OidcAuthentication.ClientId,
azureContainerRegistry.OidcAuthentication.TenantId,
azureContainerRegistry.OidcAuthentication.Audience,
azureContainerRegistry.OidcAuthentication.SubjectKeys,
)
}
case FeedTypeBuiltIn:
builtInFeed := feed.(*BuiltInFeed)
Expand All @@ -207,15 +225,15 @@ func ToFeedResource(feed IFeed) (*FeedResource, error) {
feedResource.DownloadRetryBackoffSeconds = gitHubRepositoryFeed.DownloadRetryBackoffSeconds
feedResource.FeedURI = gitHubRepositoryFeed.FeedURI
case FeedTypeGoogleContainerRegistry:
googleContainerRegistry := feed.(*AzureContainerRegistry)
googleContainerRegistry := feed.(*GoogleContainerRegistry)
feedResource.APIVersion = googleContainerRegistry.APIVersion
feedResource.FeedURI = googleContainerRegistry.FeedURI
feedResource.RegistryPath = googleContainerRegistry.RegistryPath
if googleContainerRegistry.OidcAuthentication != nil {
feedResource.AzureContainerRegistryOidcAuthentication = &AzureContainerRegistryOidcAuthentication{
Audience: googleContainerRegistry.OidcAuthentication.Audience,
SubjectKeys: googleContainerRegistry.OidcAuthentication.SubjectKeys,
}
feedResource.OidcAuthentication = NewGoogleOidcAuthentication(
googleContainerRegistry.OidcAuthentication.Audience,
googleContainerRegistry.OidcAuthentication.SubjectKeys,
)
}
case FeedTypeHelm:
helmFeed := feed.(*HelmFeed)
Expand Down
2 changes: 1 addition & 1 deletion pkg/feeds/google_container_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewGoogleContainerRegistry(name string, username string, password *core.Sen
}
}

dockerContainerRegistry, err := NewDockerContainerRegistryWithFeedType(name, FeedTypeAzureContainerRegistry)
dockerContainerRegistry, err := NewDockerContainerRegistryWithFeedType(name, FeedTypeGoogleContainerRegistry)

if err != nil {
return nil, err
Expand Down
200 changes: 200 additions & 0 deletions pkg/feeds/oidc_authentication.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package feeds

import (
"encoding/json"
)

// OidcAuthentication represents a union type that can hold any of the three OIDC authentication types
// The properties are flattened at the top level for API compatibility
type OidcAuthentication struct {
// Type indicates which OIDC authentication type this represents
Type OidcAuthenticationType `json:"Type"`

// Common fields across all OIDC types
Audience string `json:"Audience,omitempty"`
SubjectKeys []string `json:"SubjectKeys,omitempty"`

// Azure-specific fields
ClientId string `json:"ClientId,omitempty"`
TenantId string `json:"TenantId,omitempty"`

// AWS-specific fields
SessionDuration string `json:"SessionDuration,omitempty"`
RoleArn string `json:"RoleArn,omitempty"`
}

// OidcAuthenticationType represents the type of OIDC authentication
type OidcAuthenticationType string

const (
OidcAuthenticationTypeAzure OidcAuthenticationType = "Azure"
OidcAuthenticationTypeAWS OidcAuthenticationType = "AWS"
OidcAuthenticationTypeGoogle OidcAuthenticationType = "Google"
)

// NewAzureOidcAuthentication creates a new OIDC authentication for Azure
func NewAzureOidcAuthentication(clientId, tenantId, audience string, subjectKeys []string) *OidcAuthentication {
return &OidcAuthentication{
Type: OidcAuthenticationTypeAzure,
ClientId: clientId,
TenantId: tenantId,
Audience: audience,
SubjectKeys: subjectKeys,
}
}

// NewAwsOidcAuthentication creates a new OIDC authentication for AWS
func NewAwsOidcAuthentication(sessionDuration, audience, roleArn string, subjectKeys []string) *OidcAuthentication {
return &OidcAuthentication{
Type: OidcAuthenticationTypeAWS,
SessionDuration: sessionDuration,
Audience: audience,
RoleArn: roleArn,
SubjectKeys: subjectKeys,
}
}

// NewGoogleOidcAuthentication creates a new OIDC authentication for Google
func NewGoogleOidcAuthentication(audience string, subjectKeys []string) *OidcAuthentication {
return &OidcAuthentication{
Type: OidcAuthenticationTypeGoogle,
Audience: audience,
SubjectKeys: subjectKeys,
}
}

// GetAzure returns the Azure OIDC authentication data if this is an Azure type
func (o *OidcAuthentication) GetAzure() (*AzureContainerRegistryOidcAuthentication, bool) {
if o.Type == OidcAuthenticationTypeAzure {
return &AzureContainerRegistryOidcAuthentication{
ClientId: o.ClientId,
TenantId: o.TenantId,
Audience: o.Audience,
SubjectKeys: o.SubjectKeys,
}, true
}
return nil, false
}

// GetAWS returns the AWS OIDC authentication data if this is an AWS type
func (o *OidcAuthentication) GetAWS() (*AwsElasticContainerRegistryOidcAuthentication, bool) {
if o.Type == OidcAuthenticationTypeAWS {
return &AwsElasticContainerRegistryOidcAuthentication{
SessionDuration: o.SessionDuration,
Audience: o.Audience,
SubjectKeys: o.SubjectKeys,
RoleArn: o.RoleArn,
}, true
}
return nil, false
}

// GetGoogle returns the Google OIDC authentication data if this is a Google type
func (o *OidcAuthentication) GetGoogle() (*GoogleContainerRegistryOidcAuthentication, bool) {
if o.Type == OidcAuthenticationTypeGoogle {
return &GoogleContainerRegistryOidcAuthentication{
Audience: o.Audience,
SubjectKeys: o.SubjectKeys,
}, true
}
return nil, false
}

// MarshalJSON implements custom JSON marshaling to handle the union type
func (o *OidcAuthentication) MarshalJSON() ([]byte, error) {
// Create a map to hold the fields we want to serialize
result := make(map[string]interface{})

// Always include the Type field
result["Type"] = o.Type

// Add common fields
if o.Audience != "" {
result["Audience"] = o.Audience
}
if len(o.SubjectKeys) > 0 {
result["SubjectKeys"] = o.SubjectKeys
}

// Add type-specific fields based on the type
switch o.Type {
case OidcAuthenticationTypeAzure:
if o.ClientId != "" {
result["ClientId"] = o.ClientId
}
if o.TenantId != "" {
result["TenantId"] = o.TenantId
}
case OidcAuthenticationTypeAWS:
if o.SessionDuration != "" {
result["SessionDuration"] = o.SessionDuration
}
if o.RoleArn != "" {
result["RoleArn"] = o.RoleArn
}
case OidcAuthenticationTypeGoogle:
// Google only has common fields, no additional ones
}

return json.Marshal(result)
}

// UnmarshalJSON implements custom JSON unmarshaling to handle the union type
func (o *OidcAuthentication) UnmarshalJSON(data []byte) error {
if len(data) == 0 || string(data) == "null" {
*o = OidcAuthentication{}
return nil
}

// First, unmarshal into a map to inspect the fields
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}

// Discriminate based on properties. This is hacky, but we would have to add a "Type" property to the API
// To handle this properly
if _, hasClientId := raw["ClientId"]; hasClientId {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also need to check that the value isn't empty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the ClientId can be empty, but the field is still returned on the OidcAuthentication object and used as the discriminator to get the type

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, didn't realise ClientId was optional. In either case, just flagging this up as a small regression risk as we need to be careful that other shapes aren't changed at any point to include null/undefined/empty properties which could trigger the wrong conditional (seems unlikely, but possible). I'm not sure what to suggest for optional fields, though.

o.Type = OidcAuthenticationTypeAzure
} else if _, hasRoleArn := raw["RoleArn"]; hasRoleArn {
o.Type = OidcAuthenticationTypeAWS
} else {
o.Type = OidcAuthenticationTypeGoogle
}

// Now unmarshal into the appropriate struct to get all fields
switch o.Type {
case OidcAuthenticationTypeAzure:
var azure AzureContainerRegistryOidcAuthentication
if err := json.Unmarshal(data, &azure); err != nil {
return err
}
o.Type = OidcAuthenticationTypeAzure
Comment thread
IsaacCalligeros95 marked this conversation as resolved.
o.ClientId = azure.ClientId
o.TenantId = azure.TenantId
o.Audience = azure.Audience
o.SubjectKeys = azure.SubjectKeys

case OidcAuthenticationTypeAWS:
var aws AwsElasticContainerRegistryOidcAuthentication
if err := json.Unmarshal(data, &aws); err != nil {
return err
}
o.Type = OidcAuthenticationTypeAWS
o.SessionDuration = aws.SessionDuration
o.Audience = aws.Audience
o.SubjectKeys = aws.SubjectKeys
o.RoleArn = aws.RoleArn

case OidcAuthenticationTypeGoogle:
var google GoogleContainerRegistryOidcAuthentication
if err := json.Unmarshal(data, &google); err != nil {
return err
}
o.Type = OidcAuthenticationTypeGoogle
o.Audience = google.Audience
o.SubjectKeys = google.SubjectKeys
}

return nil
}
Loading
Loading