1 /** 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * SPDX-License-Identifier: Apache-2.0. 4 */ 5 6 #include <cassert> 7 #include <aws/core/Region.h> 8 #include <aws/core/utils/DNS.h> 9 #include <aws/core/utils/Outcome.h> 10 #include <aws/core/utils/StringUtils.h> 11 #include <aws/s3control/S3ControlARN.h> 12 13 namespace Aws 14 { 15 namespace S3Control 16 { S3ControlARN(const Aws::String & arn)17 S3ControlARN::S3ControlARN(const Aws::String& arn) : Utils::ARN(arn) 18 { 19 ParseARNResource(); 20 } 21 Validate(const char * clientRegion) const22 S3ControlARNOutcome S3ControlARN::Validate(const char* clientRegion) const 23 { 24 // Take pseudo region into consideration here. 25 Aws::String region = clientRegion ? clientRegion : ""; 26 Aws::StringStream ss; 27 if (this->GetResourceType() == ARNResourceType::OUTPOST && Aws::Region::IsFipsRegion(region)) 28 { 29 ss.str(""); 30 ss << "Outposts ARN do not support fips regions right now."; 31 return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false)); 32 } 33 else if (this->GetRegion() != Aws::Region::ComputeSignerRegion(clientRegion)) 34 { 35 ss.str(""); 36 ss << "Region mismatch between \"" << this->GetRegion() << "\" defined in ARN and \"" 37 << clientRegion << "\" defined in client configuration. " 38 << "You can specify AWS_S3_USE_ARN_REGION to ignore region defined in client configuration."; 39 return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", ss.str(), false)); 40 } 41 else 42 { 43 return Validate(); 44 } 45 } 46 Validate() const47 S3ControlARNOutcome S3ControlARN::Validate() const 48 { 49 Aws::String errorMessage; 50 bool success = false; 51 Aws::StringStream ss; 52 53 if (!*this) 54 { 55 errorMessage = "Invalid ARN."; 56 } 57 // Validation on partition. 58 else if (this->GetPartition().find("aws") != 0) 59 { 60 ss.str(""); 61 ss << "Invalid partition in ARN: " << this->GetPartition() << ". Valid options: aws, aws-cn, and etc."; 62 } 63 // Validation on service. 64 else if (this->GetService() != "s3" && this->GetService() != "s3-outposts") 65 { 66 ss.str(""); 67 ss << "Invalid service in ARN: " << this->GetService() << ". Valid options: s3, s3-outposts"; 68 errorMessage = ss.str(); 69 } 70 // Validation on region. 71 // TODO: Failure on different partitions. 72 else if (this->GetRegion().empty()) 73 { 74 errorMessage = "Invalid ARN with empty region."; 75 } 76 else if (!Utils::IsValidDnsLabel(this->GetRegion())) 77 { 78 ss.str(""); 79 ss << "Invalid region in ARN: " << this->GetRegion() << ". Region should be a RFC 3986 Host label."; 80 errorMessage = ss.str(); 81 } 82 else if (Aws::Region::IsFipsRegion(this->GetRegion())) 83 { 84 ss.str(""); 85 ss << "Invalid region in ARN: " << this->GetRegion() << ". FIPS region is not allowed in ARN."; 86 errorMessage = ss.str(); 87 } 88 // Validation on account ID 89 else if (!Utils::IsValidDnsLabel(this->GetAccountId())) 90 { 91 ss.str(""); 92 ss << "Invalid account ID in ARN: " << this->GetAccountId() << ". Account ID should be a RFC 3986 Host label."; 93 errorMessage = ss.str(); 94 } 95 // Validation on Outposts ARN: 96 else if (this->GetResourceType() == ARNResourceType::OUTPOST) 97 { 98 if (!Utils::IsValidDnsLabel(this->GetResourceId())) 99 { 100 ss.str(""); 101 ss << "Invalid outpost ID in Outposts ARN: " << this->GetResourceId() << ". Outpost ID should be a RFC 3986 Host label."; 102 errorMessage = ss.str(); 103 } 104 else if (this->GetSubResourceType() != ARNResourceType::ACCESSPOINT && this->GetSubResourceType() != ARNResourceType::BUCKET) 105 { 106 ss.str(""); 107 ss << "Invalid sub resource type in Outposts ARN: " << this->GetSubResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::BUCKET; 108 errorMessage = ss.str(); 109 } 110 else if (!Utils::IsValidDnsLabel(this->GetSubResourceId())) 111 { 112 ss.str(""); 113 ss << "Invalid " << this->GetSubResourceType() << " name in Outposts ARN: " << this->GetSubResourceId() << ", " << this->GetSubResourceType() << " name should be a RFC 3986 Host label."; 114 errorMessage = ss.str(); 115 } 116 else 117 { 118 success = true; 119 } 120 } 121 // Neither Access Point ARN, Bucket ARN nor Outposts ARN. 122 else 123 { 124 ss.str(""); 125 ss << "Invalid resource type in ARN: " << this->GetResourceType() << ". Valid options: " << ARNResourceType::ACCESSPOINT << ", " << ARNResourceType::OUTPOST; 126 errorMessage = ss.str(); 127 } 128 129 if (success) 130 { 131 return S3ControlARNOutcome(success); 132 } 133 else 134 { 135 return S3ControlARNOutcome(Aws::Client::AWSError<S3ControlErrors>(S3ControlErrors::VALIDATION, "VALIDATION", errorMessage, false)); 136 } 137 } 138 ParseARNResource()139 void S3ControlARN::ParseARNResource() 140 { 141 if (!*this) return; 142 143 Aws::String resource = this->GetResource(); 144 Aws::Vector<Aws::String> resourceSegments; 145 if (resource.find(':') != std::string::npos) 146 { 147 resourceSegments = Utils::StringUtils::Split(resource, ':', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES); 148 } 149 else if (resource.find('/') != std::string::npos) 150 { 151 resourceSegments = Utils::StringUtils::Split(resource, '/', 4, Utils::StringUtils::SplitOptions::INCLUDE_EMPTY_ENTRIES); 152 } 153 else 154 { 155 resourceSegments.emplace_back(resource); 156 } 157 158 switch (resourceSegments.size()) 159 { 160 case 1: 161 m_resourceId = resourceSegments[0]; 162 break; 163 case 2: 164 m_resourceType = resourceSegments[0]; 165 m_resourceId = resourceSegments[1]; 166 break; 167 case 3: 168 m_resourceType = resourceSegments[0]; 169 m_resourceId = resourceSegments[1]; 170 m_resourceQualifier = resourceSegments[2]; 171 break; 172 case 4: 173 m_resourceType = resourceSegments[0]; 174 m_resourceId = resourceSegments[1]; 175 m_subResourceType = resourceSegments[2]; 176 m_subResourceId = resourceSegments[3]; 177 break; 178 default: 179 assert(false); 180 break; 181 } 182 } 183 } 184 } 185