github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/arn/arn.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package arn 19 20 import ( 21 "fmt" 22 "regexp" 23 "strings" 24 ) 25 26 // ARN structure: 27 // 28 // arn:partition:service:region:account-id:resource-type/resource-id 29 // 30 // In this implementation, account-id is empty. 31 // 32 // Reference: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html 33 34 type arnPartition string 35 36 const ( 37 arnPartitionMinio arnPartition = "minio" 38 ) 39 40 type arnService string 41 42 const ( 43 arnServiceIAM arnService = "iam" 44 ) 45 46 type arnResourceType string 47 48 const ( 49 arnResourceTypeRole arnResourceType = "role" 50 ) 51 52 // ARN - representation of resources based on AWS ARNs. 53 type ARN struct { 54 Partition arnPartition 55 Service arnService 56 Region string 57 ResourceType arnResourceType 58 ResourceID string 59 } 60 61 // Allows english letters, numbers, '.', '-', '_' and '/'. Starts with a 62 // letter or digit. At least 1 character long. 63 var validResourceIDRegex = regexp.MustCompile(`[A-Za-z0-9_/\.-]+$`) 64 65 // NewIAMRoleARN - returns an ARN for a role in MinIO. 66 func NewIAMRoleARN(resourceID, serverRegion string) (ARN, error) { 67 if !validResourceIDRegex.MatchString(resourceID) { 68 return ARN{}, fmt.Errorf("Invalid resource ID: %s", resourceID) 69 } 70 return ARN{ 71 Partition: arnPartitionMinio, 72 Service: arnServiceIAM, 73 Region: serverRegion, 74 ResourceType: arnResourceTypeRole, 75 ResourceID: resourceID, 76 }, nil 77 } 78 79 // String - returns string representation of the ARN. 80 func (arn ARN) String() string { 81 return strings.Join( 82 []string{ 83 "arn", 84 string(arn.Partition), 85 string(arn.Service), 86 arn.Region, 87 "", // account-id is always empty in this implementation 88 string(arn.ResourceType) + "/" + arn.ResourceID, 89 }, 90 ":", 91 ) 92 } 93 94 // Parse - parses an ARN string into a type. 95 func Parse(arnStr string) (arn ARN, err error) { 96 ps := strings.Split(arnStr, ":") 97 if len(ps) != 6 || 98 ps[0] != "arn" { 99 err = fmt.Errorf("Invalid ARN string format") 100 return 101 } 102 103 if ps[1] != string(arnPartitionMinio) { 104 err = fmt.Errorf("Invalid ARN - bad partition field") 105 return 106 } 107 108 if ps[2] != string(arnServiceIAM) { 109 err = fmt.Errorf("Invalid ARN - bad service field") 110 return 111 } 112 113 // ps[3] is region and is not validated here. If the region is invalid, 114 // the ARN would not match any configured ARNs in the server. 115 116 if ps[4] != "" { 117 err = fmt.Errorf("Invalid ARN - unsupported account-id field") 118 return 119 } 120 121 res := strings.SplitN(ps[5], "/", 2) 122 if len(res) != 2 { 123 err = fmt.Errorf("Invalid ARN - resource does not contain a \"/\"") 124 return 125 } 126 127 if res[0] != string(arnResourceTypeRole) { 128 err = fmt.Errorf("Invalid ARN: resource type is invalid.") 129 return 130 } 131 132 if !validResourceIDRegex.MatchString(res[1]) { 133 err = fmt.Errorf("Invalid resource ID: %s", res[1]) 134 return 135 } 136 137 arn = ARN{ 138 Partition: arnPartitionMinio, 139 Service: arnServiceIAM, 140 Region: ps[3], 141 ResourceType: arnResourceTypeRole, 142 ResourceID: res[1], 143 } 144 return 145 }