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

Commit

Permalink
Merge pull request #13 from nodes-vapor/aws-error-parser
Browse files Browse the repository at this point in the history
AWS error parser
  • Loading branch information
BrettRToomey authored Jan 8, 2017
2 parents b5d99a3 + e70c2f0 commit 2401c35
Show file tree
Hide file tree
Showing 17 changed files with 577 additions and 99 deletions.
7 changes: 3 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import PackageDescription
let package = Package(
name: "AWS",
targets: [
Target(name: "AWS", dependencies: ["EC2", "S3", "Driver"]),
Target(name: "EC2", dependencies: ["Driver"]),
Target(name: "S3", dependencies: ["Driver"]),
Target(name: "Driver")
Target(name: "AWS", dependencies: ["EC2", "S3", "AWSSignatureV4"]),
Target(name: "EC2", dependencies: ["AWSSignatureV4"]),
Target(name: "S3", dependencies: ["AWSSignatureV4"]),
],
dependencies: [
.Package(url: "https://github.com/vapor/crypto.git", majorVersion: 1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,30 +86,23 @@ public struct AWSSignatureV4 {
}

func getCanonicalRequest(
payload: Payload,
payloadHash: String,
method: Method,
path: String,
query: String,
headers: [String : String] = [:]
canonicalHeaders: String,
signedHeaders: String
) throws -> String {
let path = try path.percentEncode(allowing: Byte.awsPathAllowed)
let query = try query.percentEncode(allowing: Byte.awsQueryAllowed)
let payloadHash = try payload.hashed()

var headers = headers
generateHeadersToSign(headers: &headers, host: host, hash: payloadHash)

let sortedHeaders = alphabetize(headers)
let canonicalHeaders = createCanonicalHeaders(sortedHeaders)
let headersToSign = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")

return [
method.rawValue,
path,
query,
canonicalHeaders,
"",
headersToSign,
signedHeaders,
payloadHash
].joined(separator: "\n")
}
Expand Down Expand Up @@ -146,26 +139,13 @@ extension AWSSignatureV4 {
}.joined(separator: "\n")
}

func signPayload(
_ payload: Payload,
mime: String?,
headers: inout [HeaderKey : String]
) throws {
/*let contentLength: Int
switch payload {
case .bytes(let bytes):
contentLength = bytes.count
default:
contentLength = 0
}
headers["Content-Length"] = "\(contentLength)"
if let mime = mime {
headers["Content-Type"] = mime
}
headers["x-amz-content-sha256"] = try payload.hashed()*/
func createAuthorizationHeader(
algorithm: String,
credentialScope: String,
signature: String,
signedHeaders: String
) -> String {
return "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=\(signedHeaders), Signature=\(signature)"
}
}

Expand All @@ -179,12 +159,22 @@ extension AWSSignatureV4 {
) throws -> [HeaderKey : String] {
let algorithm = "AWS4-HMAC-SHA256"
let credentialScope = getCredentialScope()
let payloadHash = try payload.hashed()

var headers = headers
generateHeadersToSign(headers: &headers, host: host, hash: payloadHash)

let sortedHeaders = alphabetize(headers)
let signedHeaders = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
let canonicalHeaders = createCanonicalHeaders(sortedHeaders)

let canonicalRequest = try getCanonicalRequest(
payload: payload,
payloadHash: payloadHash,
method: method,
path: path,
query: query ?? ""
query: query ?? "",
canonicalHeaders: canonicalHeaders,
signedHeaders: signedHeaders
)

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

let signature = try getSignature(stringToSign)

let authorizationHeader = "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=\(signature)"
let authorizationHeader = createAuthorizationHeader(
algorithm: algorithm,
credentialScope: credentialScope,
signature: signature,
signedHeaders: signedHeaders
)

return [
"X-Amz-Date": amzDate,
"x-amz-content-sha256": try payload.hashed(),
"x-amz-content-sha256": payloadHash,
"Authorization": authorizationHeader
]
}
Expand Down
83 changes: 83 additions & 0 deletions Sources/AWSSignatureV4/ErrorParser/AWSError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
public enum AWSError: String {
case accessDenied = "AccessDenied"
case accountProblem = "AccountProblem"
case ambiguousGrantByEmailAddress = "AmbiguousGrantByEmailAddress"
case authorizationHeaderMalformed = "AuthorizationHeaderMalformed"
case badDigest = "BadDigest"
case bucketAlreadyExists = "BucketAlreadyExists"
case bucketAlreadyOwnedByYou = "BucketAlreadyOwnedByYou"
case bucketNotEmpty = "BucketNotEmpty"
case credentialsNotSupported = "CredentialsNotSupported"
case crossLocationLoggingProhibited = "CrossLocationLoggingProhibited"
case entityTooSmall = "EntityTooSmall"
case entityTooLarge = "EntityTooLarge"
case expiredToken = "ExpiredToken"
case illegalVersioningConfigurationException = "IllegalVersioningConfigurationException"
case incompleteBody = "IncompleteBody"
case incorrectNumberOfFilesInPostRequest = "IncorrectNumberOfFilesInPostRequest"
case inlineDataTooLarge = "InlineDataTooLarge"
case internalError = "InternalError"
case invalidAccessKeyId = "InvalidAccessKeyId"
case invalidAddressingHeader = "InvalidAddressingHeader"
case invalidArgument = "InvalidArgument"
case invalidBucketName = "InvalidBucketName"
case invalidDigest = "InvalidDigest"
case invalidEncryptionAlgorithmError = "InvalidEncryptionAlgorithmError"
case invalidLocationConstraint = "InvalidLocationConstraint"
case invalidObjectState = "InvalidObjectState"
case invalidPart = "InvalidPart"
case invalidPartOrder = "InvalidPartOrder"
case invalidPayer = "InvalidPayer"
case invalidPolicyDocument = "InvalidPolicyDocument"
case invalidRange = "InvalidRange"
case invalidRequest = "InvalidRequest"
case invalidSecurity = "InvalidSecurity"
case invalidSOAPRequest = "InvalidSOAPRequest"
case invalidStorageClass = "InvalidStorageClass"
case invalidTargetBucketForLogging = "InvalidTargetBucketForLogging"
case invalidToken = "InvalidToken"
case invalidURI = "InvalidURI"
case keyTooLong = "KeyTooLong"
case malformedACLError = "MalformedACLError"
case malformedPOSTRequest = "MalformedPOSTRequest"
case malformedXML = "MalformedXML"
case maxMessageLengthExceeded = "MaxMessageLengthExceeded"
case maxPostPreDataLengthExceededError = "MaxPostPreDataLengthExceededError"
case metadataTooLarge = "MetadataTooLarge"
case methodNotAllowed = "MethodNotAllowed"
case missingAttachment = "MissingAttachment"
case missingContentLength = "MissingContentLength"
case missingRequestBodyError = "MissingRequestBodyError"
case missingSecurityElement = "MissingSecurityElement"
case missingSecurityHeader = "MissingSecurityHeader"
case noLoggingStatusForKey = "NoLoggingStatusForKey"
case noSuchBucket = "NoSuchBucket"
case noSuchKey = "NoSuchKey"
case noSuchLifecycleConfiguration = "NoSuchLifecycleConfiguration"
case noSuchUpload = "NoSuchUpload"
case noSuchVersion = "NoSuchVersion"
case notImplemented = "NotImplemented"
case notSignedUp = "NotSignedUp"
case noSuchBucketPolicy = "NoSuchBucketPolicy"
case operationAborted = "OperationAborted"
case peramentRedirect = "PeramentRedirect"
case preconditionFailed = "PreconditionFailed"
case redirect = "Redirect"
case restoreAlreadyInProgress = "RestoreAlreadyInProgress"
case requestIsNotMultiPartContent = "RequestIsNotMultiPartContent"
case requestTimeout = "RequestTimeout"
case requestTimeTooSkewed = "RequestTimeTooSkewed"
case requestTorrentOfBucketError = "RequestTorrentOfBucketError"
case signatureDoesNotMatch = "SignatureDoesNotMatch"
case serviceUnavailable = "ServiceUnavailable"
case slowDown = "SlowDown"
case temporaryRedirect = "TemporaryRedirect"
case tokenRefreshRequired = "TokenRefreshRequired"
case tooManyBuckets = "TooManyBuckets"
case unexpectedContent = "UnexpectedContent"
case unresolvableGrantByEmailAddress = "UnresolvableGrantByEmailAddress"
case userKeyMustBeSpecified = "UserKeyMustBeSpecified"
}

extension AWSError: Error {
}
172 changes: 172 additions & 0 deletions Sources/AWSSignatureV4/ErrorParser/ErrorParser+Grammar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import Core

extension ErrorParser {
static let awsGrammar: Trie<AWSError> = {
let trie = Trie<AWSError>()

insert(into: trie, .accessDenied)
insert(into: trie, .accountProblem)
insert(into: trie, .ambiguousGrantByEmailAddress)
insert(into: trie, .authorizationHeaderMalformed)
insert(into: trie, .badDigest)
insert(into: trie, .bucketAlreadyExists)
insert(into: trie, .bucketAlreadyOwnedByYou)
insert(into: trie, .bucketNotEmpty)
insert(into: trie, .credentialsNotSupported)
insert(into: trie, .crossLocationLoggingProhibited)
insert(into: trie, .entityTooSmall)
insert(into: trie, .entityTooLarge)
insert(into: trie, .expiredToken)
insert(into: trie, .illegalVersioningConfigurationException)
insert(into: trie, .incompleteBody)
insert(into: trie, .incorrectNumberOfFilesInPostRequest)
insert(into: trie, .inlineDataTooLarge)
insert(into: trie, .internalError)
insert(into: trie, .invalidAccessKeyId)
insert(into: trie, .invalidAddressingHeader)
insert(into: trie, .invalidArgument)
insert(into: trie, .invalidBucketName)
insert(into: trie, .invalidDigest)
insert(into: trie, .invalidEncryptionAlgorithmError)
insert(into: trie, .invalidLocationConstraint)
insert(into: trie, .invalidObjectState)
insert(into: trie, .invalidPart)
insert(into: trie, .invalidPartOrder)
insert(into: trie, .invalidPayer)
insert(into: trie, .invalidPolicyDocument)
insert(into: trie, .invalidRange)
insert(into: trie, .invalidRequest)
insert(into: trie, .invalidSecurity)
insert(into: trie, .invalidSOAPRequest)
insert(into: trie, .invalidStorageClass)
insert(into: trie, .invalidTargetBucketForLogging)
insert(into: trie, .invalidToken)
insert(into: trie, .invalidURI)
insert(into: trie, .keyTooLong)
insert(into: trie, .malformedACLError)
insert(into: trie, .malformedPOSTRequest)
insert(into: trie, .malformedXML)
insert(into: trie, .maxMessageLengthExceeded)
insert(into: trie, .maxPostPreDataLengthExceededError)
insert(into: trie, .metadataTooLarge)
insert(into: trie, .methodNotAllowed)
insert(into: trie, .missingAttachment)
insert(into: trie, .missingContentLength)
insert(into: trie, .missingRequestBodyError)
insert(into: trie, .missingSecurityElement)
insert(into: trie, .missingSecurityHeader)
insert(into: trie, .noLoggingStatusForKey)
insert(into: trie, .noSuchBucket)
insert(into: trie, .noSuchKey)
insert(into: trie, .noSuchLifecycleConfiguration)
insert(into: trie, .noSuchUpload)
insert(into: trie, .noSuchVersion)
insert(into: trie, .notImplemented)
insert(into: trie, .notSignedUp)
insert(into: trie, .noSuchBucketPolicy)
insert(into: trie, .operationAborted)
insert(into: trie, .peramentRedirect)
insert(into: trie, .preconditionFailed)
insert(into: trie, .redirect)
insert(into: trie, .restoreAlreadyInProgress)
insert(into: trie, .requestIsNotMultiPartContent)
insert(into: trie, .requestTimeout)
insert(into: trie, .requestTimeTooSkewed)
insert(into: trie, .requestTorrentOfBucketError)
insert(into: trie, .signatureDoesNotMatch)
insert(into: trie, .serviceUnavailable)
insert(into: trie, .slowDown)
insert(into: trie, .temporaryRedirect)
insert(into: trie, .tokenRefreshRequired)
insert(into: trie, .tooManyBuckets)
insert(into: trie, .unexpectedContent)
insert(into: trie, .unresolvableGrantByEmailAddress)
insert(into: trie, .userKeyMustBeSpecified)

return trie
}()

static func insert(into trie: Trie<AWSError>, _ error: AWSError) {
trie.insert(error, for: error.rawValue.bytes)
}
}

/*
case accessDenied
case accountProblem
case ambiguousGrantByEmailAddress
case badDigest
case bucketAlreadyExists
case bucketAlreadyOwnedByYou
case bucketNotEmpty
case credentialsNotSupported
case crossLocationLoggingProhibited
case entityTooSmall
case entityTooLarge
case expiredToken
case illegalVersioningConfigurationException
case incompleteBody
case incorrectNumberOfFilesInPostRequest
case inlineDataTooLarge
case internalError
case invalidAccessKeyId
case invalidAddressingHeader
case invalidArgument
case invalidBucketName
case invalidDigest
case invalidEncryptionAlgorithmError
case invalidLocationConstraint
case invalidObjectState
case invalidPart
case invalidPartOrder
case invalidPayer
case invalidPolicyDocument
case invalidRange
case invalidRequest
case invalidSecurity
case invalidSOAPRequest
case invalidStorageClass
case invalidTargetBucketForLogging
case invalidToken
case invalidURI
case keyTooLong
case malformedACLError
case malformedPOSTRequest
case malformedXML
case maxMessageLengthExceeded
case maxPostPreDataLengthExceededError
case metadataTooLarge
case methodNotAllowed
case missingAttachment
case missingContentLength
case missingRequestBodyError
case missingSecurityElement
case missingSecurityHeader
case noLoggingStatusForKey
case noSuchBucket
case noSuchKey
case noSuchLifecycleConfiguration
case noSuchUpload
case noSuchVersion
case notImplemented
case notSignedUp
case noSuchBucketPolicy
case operationAborted
case peramentRedirect
case preconditionFailed
case redirect
case restoreAlreadyInProgress
case requestIsNotMultiPartContent
case requestTimeout
case requestTimeTooSkewed
case requestTorrentOfBucketError
case signatureDoesNotMatch
case serviceUnavailable
case slowDown
case temporaryRedirect
case tokenRefreshRequired
case tooManyBuckets
case unexpectedContent
case unresolvableGrantByEmailAddress
case userKeyMustBeSpecified
*/
Loading

0 comments on commit 2401c35

Please sign in to comment.