1package arn
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/aws/aws-sdk-go/aws/arn"
8)
9
10var supportedServiceARN = []string{
11	"s3",
12	"s3-outposts",
13	"s3-object-lambda",
14}
15
16func isSupportedServiceARN(service string) bool {
17	for _, name := range supportedServiceARN {
18		if name == service {
19			return true
20		}
21	}
22	return false
23}
24
25// Resource provides the interfaces abstracting ARNs of specific resource
26// types.
27type Resource interface {
28	GetARN() arn.ARN
29	String() string
30}
31
32// ResourceParser provides the function for parsing an ARN's resource
33// component into a typed resource.
34type ResourceParser func(arn.ARN) (Resource, error)
35
36// ParseResource parses an AWS ARN into a typed resource for the S3 API.
37func ParseResource(s string, resParser ResourceParser) (resARN Resource, err error) {
38	a, err := arn.Parse(s)
39	if err != nil {
40		return nil, err
41	}
42
43	if len(a.Partition) == 0 {
44		return nil, InvalidARNError{ARN: a, Reason: "partition not set"}
45	}
46
47	if !isSupportedServiceARN(a.Service) {
48		return nil, InvalidARNError{ARN: a, Reason: "service is not supported"}
49	}
50
51	if strings.HasPrefix(a.Region, "fips-") || strings.HasSuffix(a.Region, "-fips") {
52		return nil, InvalidARNError{ARN: a, Reason: "FIPS region not allowed in ARN"}
53	}
54
55	if len(a.Resource) == 0 {
56		return nil, InvalidARNError{ARN: a, Reason: "resource not set"}
57	}
58
59	return resParser(a)
60}
61
62// SplitResource splits the resource components by the ARN resource delimiters.
63func SplitResource(v string) []string {
64	var parts []string
65	var offset int
66
67	for offset <= len(v) {
68		idx := strings.IndexAny(v[offset:], "/:")
69		if idx < 0 {
70			parts = append(parts, v[offset:])
71			break
72		}
73		parts = append(parts, v[offset:idx+offset])
74		offset += idx + 1
75	}
76
77	return parts
78}
79
80// IsARN returns whether the given string is an ARN
81func IsARN(s string) bool {
82	return arn.IsARN(s)
83}
84
85// InvalidARNError provides the error for an invalid ARN error.
86type InvalidARNError struct {
87	ARN    arn.ARN
88	Reason string
89}
90
91// Error returns a string denoting the occurred InvalidARNError
92func (e InvalidARNError) Error() string {
93	return fmt.Sprintf("invalid Amazon %s ARN, %s, %s", e.ARN.Service, e.Reason, e.ARN.String())
94}
95