Skip to content
This repository was archived by the owner on Apr 20, 2024. It is now read-only.

Commit 2401c35

Browse files
authored
Merge pull request #13 from nodes-vapor/aws-error-parser
AWS error parser
2 parents b5d99a3 + e70c2f0 commit 2401c35

17 files changed

+577
-99
lines changed

Package.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import PackageDescription
33
let package = Package(
44
name: "AWS",
55
targets: [
6-
Target(name: "AWS", dependencies: ["EC2", "S3", "Driver"]),
7-
Target(name: "EC2", dependencies: ["Driver"]),
8-
Target(name: "S3", dependencies: ["Driver"]),
9-
Target(name: "Driver")
6+
Target(name: "AWS", dependencies: ["EC2", "S3", "AWSSignatureV4"]),
7+
Target(name: "EC2", dependencies: ["AWSSignatureV4"]),
8+
Target(name: "S3", dependencies: ["AWSSignatureV4"]),
109
],
1110
dependencies: [
1211
.Package(url: "https://github.com/vapor/crypto.git", majorVersion: 1),

Sources/Driver/Authentication/AWSSignatureV4.swift Sources/AWSSignatureV4/AWSSignatureV4.swift

+30-35
Original file line numberDiff line numberDiff line change
@@ -86,30 +86,23 @@ public struct AWSSignatureV4 {
8686
}
8787

8888
func getCanonicalRequest(
89-
payload: Payload,
89+
payloadHash: String,
9090
method: Method,
9191
path: String,
9292
query: String,
93-
headers: [String : String] = [:]
93+
canonicalHeaders: String,
94+
signedHeaders: String
9495
) throws -> String {
9596
let path = try path.percentEncode(allowing: Byte.awsPathAllowed)
9697
let query = try query.percentEncode(allowing: Byte.awsQueryAllowed)
97-
let payloadHash = try payload.hashed()
98-
99-
var headers = headers
100-
generateHeadersToSign(headers: &headers, host: host, hash: payloadHash)
101-
102-
let sortedHeaders = alphabetize(headers)
103-
let canonicalHeaders = createCanonicalHeaders(sortedHeaders)
104-
let headersToSign = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
10598

10699
return [
107100
method.rawValue,
108101
path,
109102
query,
110103
canonicalHeaders,
111104
"",
112-
headersToSign,
105+
signedHeaders,
113106
payloadHash
114107
].joined(separator: "\n")
115108
}
@@ -146,26 +139,13 @@ extension AWSSignatureV4 {
146139
}.joined(separator: "\n")
147140
}
148141

149-
func signPayload(
150-
_ payload: Payload,
151-
mime: String?,
152-
headers: inout [HeaderKey : String]
153-
) throws {
154-
/*let contentLength: Int
155-
156-
switch payload {
157-
case .bytes(let bytes):
158-
contentLength = bytes.count
159-
default:
160-
contentLength = 0
161-
}
162-
163-
headers["Content-Length"] = "\(contentLength)"
164-
if let mime = mime {
165-
headers["Content-Type"] = mime
166-
}
167-
168-
headers["x-amz-content-sha256"] = try payload.hashed()*/
142+
func createAuthorizationHeader(
143+
algorithm: String,
144+
credentialScope: String,
145+
signature: String,
146+
signedHeaders: String
147+
) -> String {
148+
return "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=\(signedHeaders), Signature=\(signature)"
169149
}
170150
}
171151

@@ -179,12 +159,22 @@ extension AWSSignatureV4 {
179159
) throws -> [HeaderKey : String] {
180160
let algorithm = "AWS4-HMAC-SHA256"
181161
let credentialScope = getCredentialScope()
162+
let payloadHash = try payload.hashed()
163+
164+
var headers = headers
165+
generateHeadersToSign(headers: &headers, host: host, hash: payloadHash)
166+
167+
let sortedHeaders = alphabetize(headers)
168+
let signedHeaders = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
169+
let canonicalHeaders = createCanonicalHeaders(sortedHeaders)
182170

183171
let canonicalRequest = try getCanonicalRequest(
184-
payload: payload,
172+
payloadHash: payloadHash,
185173
method: method,
186174
path: path,
187-
query: query ?? ""
175+
query: query ?? "",
176+
canonicalHeaders: canonicalHeaders,
177+
signedHeaders: signedHeaders
188178
)
189179

190180
let canonicalHash = try Hash.make(.sha256, canonicalRequest).hexString
@@ -198,11 +188,16 @@ extension AWSSignatureV4 {
198188

199189
let signature = try getSignature(stringToSign)
200190

201-
let authorizationHeader = "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=\(signature)"
191+
let authorizationHeader = createAuthorizationHeader(
192+
algorithm: algorithm,
193+
credentialScope: credentialScope,
194+
signature: signature,
195+
signedHeaders: signedHeaders
196+
)
202197

203198
return [
204199
"X-Amz-Date": amzDate,
205-
"x-amz-content-sha256": try payload.hashed(),
200+
"x-amz-content-sha256": payloadHash,
206201
"Authorization": authorizationHeader
207202
]
208203
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
public enum AWSError: String {
2+
case accessDenied = "AccessDenied"
3+
case accountProblem = "AccountProblem"
4+
case ambiguousGrantByEmailAddress = "AmbiguousGrantByEmailAddress"
5+
case authorizationHeaderMalformed = "AuthorizationHeaderMalformed"
6+
case badDigest = "BadDigest"
7+
case bucketAlreadyExists = "BucketAlreadyExists"
8+
case bucketAlreadyOwnedByYou = "BucketAlreadyOwnedByYou"
9+
case bucketNotEmpty = "BucketNotEmpty"
10+
case credentialsNotSupported = "CredentialsNotSupported"
11+
case crossLocationLoggingProhibited = "CrossLocationLoggingProhibited"
12+
case entityTooSmall = "EntityTooSmall"
13+
case entityTooLarge = "EntityTooLarge"
14+
case expiredToken = "ExpiredToken"
15+
case illegalVersioningConfigurationException = "IllegalVersioningConfigurationException"
16+
case incompleteBody = "IncompleteBody"
17+
case incorrectNumberOfFilesInPostRequest = "IncorrectNumberOfFilesInPostRequest"
18+
case inlineDataTooLarge = "InlineDataTooLarge"
19+
case internalError = "InternalError"
20+
case invalidAccessKeyId = "InvalidAccessKeyId"
21+
case invalidAddressingHeader = "InvalidAddressingHeader"
22+
case invalidArgument = "InvalidArgument"
23+
case invalidBucketName = "InvalidBucketName"
24+
case invalidDigest = "InvalidDigest"
25+
case invalidEncryptionAlgorithmError = "InvalidEncryptionAlgorithmError"
26+
case invalidLocationConstraint = "InvalidLocationConstraint"
27+
case invalidObjectState = "InvalidObjectState"
28+
case invalidPart = "InvalidPart"
29+
case invalidPartOrder = "InvalidPartOrder"
30+
case invalidPayer = "InvalidPayer"
31+
case invalidPolicyDocument = "InvalidPolicyDocument"
32+
case invalidRange = "InvalidRange"
33+
case invalidRequest = "InvalidRequest"
34+
case invalidSecurity = "InvalidSecurity"
35+
case invalidSOAPRequest = "InvalidSOAPRequest"
36+
case invalidStorageClass = "InvalidStorageClass"
37+
case invalidTargetBucketForLogging = "InvalidTargetBucketForLogging"
38+
case invalidToken = "InvalidToken"
39+
case invalidURI = "InvalidURI"
40+
case keyTooLong = "KeyTooLong"
41+
case malformedACLError = "MalformedACLError"
42+
case malformedPOSTRequest = "MalformedPOSTRequest"
43+
case malformedXML = "MalformedXML"
44+
case maxMessageLengthExceeded = "MaxMessageLengthExceeded"
45+
case maxPostPreDataLengthExceededError = "MaxPostPreDataLengthExceededError"
46+
case metadataTooLarge = "MetadataTooLarge"
47+
case methodNotAllowed = "MethodNotAllowed"
48+
case missingAttachment = "MissingAttachment"
49+
case missingContentLength = "MissingContentLength"
50+
case missingRequestBodyError = "MissingRequestBodyError"
51+
case missingSecurityElement = "MissingSecurityElement"
52+
case missingSecurityHeader = "MissingSecurityHeader"
53+
case noLoggingStatusForKey = "NoLoggingStatusForKey"
54+
case noSuchBucket = "NoSuchBucket"
55+
case noSuchKey = "NoSuchKey"
56+
case noSuchLifecycleConfiguration = "NoSuchLifecycleConfiguration"
57+
case noSuchUpload = "NoSuchUpload"
58+
case noSuchVersion = "NoSuchVersion"
59+
case notImplemented = "NotImplemented"
60+
case notSignedUp = "NotSignedUp"
61+
case noSuchBucketPolicy = "NoSuchBucketPolicy"
62+
case operationAborted = "OperationAborted"
63+
case peramentRedirect = "PeramentRedirect"
64+
case preconditionFailed = "PreconditionFailed"
65+
case redirect = "Redirect"
66+
case restoreAlreadyInProgress = "RestoreAlreadyInProgress"
67+
case requestIsNotMultiPartContent = "RequestIsNotMultiPartContent"
68+
case requestTimeout = "RequestTimeout"
69+
case requestTimeTooSkewed = "RequestTimeTooSkewed"
70+
case requestTorrentOfBucketError = "RequestTorrentOfBucketError"
71+
case signatureDoesNotMatch = "SignatureDoesNotMatch"
72+
case serviceUnavailable = "ServiceUnavailable"
73+
case slowDown = "SlowDown"
74+
case temporaryRedirect = "TemporaryRedirect"
75+
case tokenRefreshRequired = "TokenRefreshRequired"
76+
case tooManyBuckets = "TooManyBuckets"
77+
case unexpectedContent = "UnexpectedContent"
78+
case unresolvableGrantByEmailAddress = "UnresolvableGrantByEmailAddress"
79+
case userKeyMustBeSpecified = "UserKeyMustBeSpecified"
80+
}
81+
82+
extension AWSError: Error {
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import Core
2+
3+
extension ErrorParser {
4+
static let awsGrammar: Trie<AWSError> = {
5+
let trie = Trie<AWSError>()
6+
7+
insert(into: trie, .accessDenied)
8+
insert(into: trie, .accountProblem)
9+
insert(into: trie, .ambiguousGrantByEmailAddress)
10+
insert(into: trie, .authorizationHeaderMalformed)
11+
insert(into: trie, .badDigest)
12+
insert(into: trie, .bucketAlreadyExists)
13+
insert(into: trie, .bucketAlreadyOwnedByYou)
14+
insert(into: trie, .bucketNotEmpty)
15+
insert(into: trie, .credentialsNotSupported)
16+
insert(into: trie, .crossLocationLoggingProhibited)
17+
insert(into: trie, .entityTooSmall)
18+
insert(into: trie, .entityTooLarge)
19+
insert(into: trie, .expiredToken)
20+
insert(into: trie, .illegalVersioningConfigurationException)
21+
insert(into: trie, .incompleteBody)
22+
insert(into: trie, .incorrectNumberOfFilesInPostRequest)
23+
insert(into: trie, .inlineDataTooLarge)
24+
insert(into: trie, .internalError)
25+
insert(into: trie, .invalidAccessKeyId)
26+
insert(into: trie, .invalidAddressingHeader)
27+
insert(into: trie, .invalidArgument)
28+
insert(into: trie, .invalidBucketName)
29+
insert(into: trie, .invalidDigest)
30+
insert(into: trie, .invalidEncryptionAlgorithmError)
31+
insert(into: trie, .invalidLocationConstraint)
32+
insert(into: trie, .invalidObjectState)
33+
insert(into: trie, .invalidPart)
34+
insert(into: trie, .invalidPartOrder)
35+
insert(into: trie, .invalidPayer)
36+
insert(into: trie, .invalidPolicyDocument)
37+
insert(into: trie, .invalidRange)
38+
insert(into: trie, .invalidRequest)
39+
insert(into: trie, .invalidSecurity)
40+
insert(into: trie, .invalidSOAPRequest)
41+
insert(into: trie, .invalidStorageClass)
42+
insert(into: trie, .invalidTargetBucketForLogging)
43+
insert(into: trie, .invalidToken)
44+
insert(into: trie, .invalidURI)
45+
insert(into: trie, .keyTooLong)
46+
insert(into: trie, .malformedACLError)
47+
insert(into: trie, .malformedPOSTRequest)
48+
insert(into: trie, .malformedXML)
49+
insert(into: trie, .maxMessageLengthExceeded)
50+
insert(into: trie, .maxPostPreDataLengthExceededError)
51+
insert(into: trie, .metadataTooLarge)
52+
insert(into: trie, .methodNotAllowed)
53+
insert(into: trie, .missingAttachment)
54+
insert(into: trie, .missingContentLength)
55+
insert(into: trie, .missingRequestBodyError)
56+
insert(into: trie, .missingSecurityElement)
57+
insert(into: trie, .missingSecurityHeader)
58+
insert(into: trie, .noLoggingStatusForKey)
59+
insert(into: trie, .noSuchBucket)
60+
insert(into: trie, .noSuchKey)
61+
insert(into: trie, .noSuchLifecycleConfiguration)
62+
insert(into: trie, .noSuchUpload)
63+
insert(into: trie, .noSuchVersion)
64+
insert(into: trie, .notImplemented)
65+
insert(into: trie, .notSignedUp)
66+
insert(into: trie, .noSuchBucketPolicy)
67+
insert(into: trie, .operationAborted)
68+
insert(into: trie, .peramentRedirect)
69+
insert(into: trie, .preconditionFailed)
70+
insert(into: trie, .redirect)
71+
insert(into: trie, .restoreAlreadyInProgress)
72+
insert(into: trie, .requestIsNotMultiPartContent)
73+
insert(into: trie, .requestTimeout)
74+
insert(into: trie, .requestTimeTooSkewed)
75+
insert(into: trie, .requestTorrentOfBucketError)
76+
insert(into: trie, .signatureDoesNotMatch)
77+
insert(into: trie, .serviceUnavailable)
78+
insert(into: trie, .slowDown)
79+
insert(into: trie, .temporaryRedirect)
80+
insert(into: trie, .tokenRefreshRequired)
81+
insert(into: trie, .tooManyBuckets)
82+
insert(into: trie, .unexpectedContent)
83+
insert(into: trie, .unresolvableGrantByEmailAddress)
84+
insert(into: trie, .userKeyMustBeSpecified)
85+
86+
return trie
87+
}()
88+
89+
static func insert(into trie: Trie<AWSError>, _ error: AWSError) {
90+
trie.insert(error, for: error.rawValue.bytes)
91+
}
92+
}
93+
94+
/*
95+
case accessDenied
96+
case accountProblem
97+
case ambiguousGrantByEmailAddress
98+
case badDigest
99+
case bucketAlreadyExists
100+
case bucketAlreadyOwnedByYou
101+
case bucketNotEmpty
102+
case credentialsNotSupported
103+
case crossLocationLoggingProhibited
104+
case entityTooSmall
105+
case entityTooLarge
106+
case expiredToken
107+
case illegalVersioningConfigurationException
108+
case incompleteBody
109+
case incorrectNumberOfFilesInPostRequest
110+
case inlineDataTooLarge
111+
case internalError
112+
case invalidAccessKeyId
113+
case invalidAddressingHeader
114+
case invalidArgument
115+
case invalidBucketName
116+
case invalidDigest
117+
case invalidEncryptionAlgorithmError
118+
case invalidLocationConstraint
119+
case invalidObjectState
120+
case invalidPart
121+
case invalidPartOrder
122+
case invalidPayer
123+
case invalidPolicyDocument
124+
case invalidRange
125+
case invalidRequest
126+
case invalidSecurity
127+
case invalidSOAPRequest
128+
case invalidStorageClass
129+
case invalidTargetBucketForLogging
130+
case invalidToken
131+
case invalidURI
132+
case keyTooLong
133+
case malformedACLError
134+
case malformedPOSTRequest
135+
case malformedXML
136+
case maxMessageLengthExceeded
137+
case maxPostPreDataLengthExceededError
138+
case metadataTooLarge
139+
case methodNotAllowed
140+
case missingAttachment
141+
case missingContentLength
142+
case missingRequestBodyError
143+
case missingSecurityElement
144+
case missingSecurityHeader
145+
case noLoggingStatusForKey
146+
case noSuchBucket
147+
case noSuchKey
148+
case noSuchLifecycleConfiguration
149+
case noSuchUpload
150+
case noSuchVersion
151+
case notImplemented
152+
case notSignedUp
153+
case noSuchBucketPolicy
154+
case operationAborted
155+
case peramentRedirect
156+
case preconditionFailed
157+
case redirect
158+
case restoreAlreadyInProgress
159+
case requestIsNotMultiPartContent
160+
case requestTimeout
161+
case requestTimeTooSkewed
162+
case requestTorrentOfBucketError
163+
case signatureDoesNotMatch
164+
case serviceUnavailable
165+
case slowDown
166+
case temporaryRedirect
167+
case tokenRefreshRequired
168+
case tooManyBuckets
169+
case unexpectedContent
170+
case unresolvableGrantByEmailAddress
171+
case userKeyMustBeSpecified
172+
*/

0 commit comments

Comments
 (0)