package arn import ( "strings" "github.com/aws/aws-sdk-go-v2/aws/arn" ) // OutpostARN interface that should be satisfied by outpost ARNs type OutpostARN interface { Resource GetOutpostID() string } // ParseOutpostARNResource will parse a provided ARNs resource using the appropriate ARN format // and return a specific OutpostARN type // // Currently supported outpost ARN formats: // * Outpost AccessPoint ARN format: // - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/accesspoint/{accesspointName} // - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/accesspoint/myaccesspoint // // * Outpost Bucket ARN format: // - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/bucket/{bucketName} // - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/bucket/mybucket // // Other outpost ARN formats may be supported and added in the future. func ParseOutpostARNResource(a arn.ARN, resParts []string) (OutpostARN, error) { if len(a.Region) == 0 { return nil, InvalidARNError{ARN: a, Reason: "region not set"} } if isFIPS(a.Region) { return nil, InvalidARNError{ARN: a, Reason: "FIPS region not allowed in ARN"} } if len(a.AccountID) == 0 { return nil, InvalidARNError{ARN: a, Reason: "account-id not set"} } // verify if outpost id is present and valid if len(resParts) == 0 || len(strings.TrimSpace(resParts[0])) == 0 { return nil, InvalidARNError{ARN: a, Reason: "outpost resource-id not set"} } // verify possible resource type exists if len(resParts) < 3 { return nil, InvalidARNError{ ARN: a, Reason: "incomplete outpost resource type. Expected bucket or access-point resource to be present", } } // Since we know this is a OutpostARN fetch outpostID outpostID := strings.TrimSpace(resParts[0]) switch resParts[1] { case "accesspoint": accesspointARN, err := ParseAccessPointResource(a, resParts[2:]) if err != nil { return OutpostAccessPointARN{}, err } return OutpostAccessPointARN{ AccessPointARN: accesspointARN, OutpostID: outpostID, }, nil case "bucket": bucketName, err := parseBucketResource(a, resParts[2:]) if err != nil { return nil, err } return OutpostBucketARN{ ARN: a, BucketName: bucketName, OutpostID: outpostID, }, nil default: return nil, InvalidARNError{ARN: a, Reason: "unknown resource set for outpost ARN"} } } // OutpostAccessPointARN represents outpost access point ARN. type OutpostAccessPointARN struct { AccessPointARN OutpostID string } // GetOutpostID returns the outpost id of outpost access point arn func (o OutpostAccessPointARN) GetOutpostID() string { return o.OutpostID } // OutpostBucketARN represents the outpost bucket ARN. type OutpostBucketARN struct { arn.ARN BucketName string OutpostID string } // GetOutpostID returns the outpost id of outpost bucket arn func (o OutpostBucketARN) GetOutpostID() string { return o.OutpostID } // GetARN retrives the base ARN from outpost bucket ARN resource func (o OutpostBucketARN) GetARN() arn.ARN { return o.ARN } // parseBucketResource attempts to parse the ARN's bucket resource and retrieve the // bucket resource id. // // parseBucketResource only parses the bucket resource id. func parseBucketResource(a arn.ARN, resParts []string) (bucketName string, err error) { if len(resParts) == 0 { return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"} } if len(resParts) > 1 { return bucketName, InvalidARNError{ARN: a, Reason: "sub resource not supported"} } bucketName = strings.TrimSpace(resParts[0]) if len(bucketName) == 0 { return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"} } return bucketName, err }