From 528112a3e25ac7770aa5110018aa6344eeaad12e Mon Sep 17 00:00:00 2001 From: Dapeng Gao <31944689+dpgao@users.noreply.github.com> Date: Wed, 9 Jan 2019 16:31:56 +0800 Subject: [PATCH] Allow custom region names (#32) --- Sources/S3/Extensions/S3+Bucket.swift | 15 +- Sources/S3Signer/Region.swift | 188 +++++++++--------------- Sources/S3Signer/S3Signer+Private.swift | 7 +- 3 files changed, 78 insertions(+), 132 deletions(-) diff --git a/Sources/S3/Extensions/S3+Bucket.swift b/Sources/S3/Extensions/S3+Bucket.swift index ac78654..c1c7749 100644 --- a/Sources/S3/Extensions/S3+Bucket.swift +++ b/Sources/S3/Extensions/S3+Bucket.swift @@ -34,17 +34,12 @@ public extension S3 { return Region.usEast1 } else { // Split bucket.s3.region.amazonaws.com into parts - var parts = endpoint.split(separator: ".") - // Remove .com - parts.removeLast() - // Remove .amazonaws - parts.removeLast() - // Get region (lat part) - let regionString = String(parts.removeLast()).lowercased() - guard let region = Region(rawValue: regionString) else { + // Drop .com and .amazonaws + // Get region (last part) + guard let regionString = endpoint.split(separator: ".").dropLast(2).last?.lowercased() else { throw Error.badResponse(response) } - return region + return Region(name: .init(regionString)) } } else { throw Error.badResponse(response) @@ -74,7 +69,7 @@ public extension S3 { let content = """ - \(region.name.rawValue) + \(region.name) """ diff --git a/Sources/S3Signer/Region.swift b/Sources/S3Signer/Region.swift index a32ac1f..f5f590f 100755 --- a/Sources/S3Signer/Region.swift +++ b/Sources/S3Signer/Region.swift @@ -1,71 +1,79 @@ -import Foundation - - /// AWS Region public struct Region { - /// name of the region, see RegionName - public let name: RegionName - + /// name of the region, see Name + public let name: Name + /// name of the custom host, can contain IP and/or port (e.g. 127.0.0.1:9000) public let hostName: String? - + /// use TLS/https (defaults to true) public let useTLS: Bool - public enum RegionName : String, Codable { + public struct Name: ExpressibleByStringLiteral, LosslessStringConvertible { + /// US East (N. Virginia) - case usEast1 = "us-east-1" + public static let usEast1: Name = "us-east-1" /// US East (Ohio) - case usEast2 = "us-east-2" + public static let usEast2: Name = "us-east-2" /// US West (N. California) - case usWest1 = "us-west-1" + public static let usWest1: Name = "us-west-1" /// US West (Oregon) - case usWest2 = "us-west-2" + public static let usWest2: Name = "us-west-2" /// Canada (Central) - case caCentral1 = "ca-central-1" + public static let caCentral1: Name = "ca-central-1" /// EU (Frankfurt) - case euCentral1 = "eu-central-1" + public static let euCentral1: Name = "eu-central-1" /// EU (Ireland) - case euWest1 = "eu-west-1" + public static let euWest1: Name = "eu-west-1" /// EU (London) - case euWest2 = "eu-west-2" + public static let euWest2: Name = "eu-west-2" /// EU (Paris) - case euWest3 = "eu-west-3" + public static let euWest3: Name = "eu-west-3" /// Asia Pacific (Tokyo) - case apNortheast1 = "ap-northeast-1" + public static let apNortheast1: Name = "ap-northeast-1" /// Asia Pacific (Seoul) - case apNortheast2 = "ap-northeast-2" + public static let apNortheast2: Name = "ap-northeast-2" /// Asia Pacific (Osaka-Local) - case apNortheast3 = "ap-northeast-3" + public static let apNortheast3: Name = "ap-northeast-3" /// Asia Pacific (Singapore) - case apSoutheast1 = "ap-southeast-1" + public static let apSoutheast1: Name = "ap-southeast-1" /// Asia Pacific (Sydney) - case apSoutheast2 = "ap-southeast-2" + public static let apSoutheast2: Name = "ap-southeast-2" /// Asia Pacific (Mumbai) - case apSouth1 = "ap-south-1" + public static let apSouth1: Name = "ap-south-1" /// South America (São Paulo) - case saEast1 = "sa-east-1" + public static let saEast1: Name = "sa-east-1" + + public let description: String + + public init(_ value: String) { + self.description = value + } + + public init(stringLiteral value: String) { + self.init(value) + } } - + /// initializer for a (custom) region. If you use a custom hostName, you /// still need a region (e.g. use usEast1 for Minio) - public init(name: RegionName, hostName: String? = nil, useTLS: Bool = true) { + public init(name: Name, hostName: String? = nil, useTLS: Bool = true) { self.name = name self.hostName = hostName self.useTLS = useTLS @@ -77,125 +85,71 @@ extension Region { /// Base URL / Host public var host: String { - if let host = hostName { - return host - } - return "s3.\(name.rawValue).amazonaws.com" + return hostName ?? "s3.\(name).amazonaws.com" } } extension Region { - public init?(rawValue: String) { - guard let name = RegionName(rawValue: rawValue) else { - return nil - } - - self.init(name: name) - } /// convenience var for US East (N. Virginia) - public static var usEast1: Region { - return Region(name: RegionName.usEast1) - } - + public static let usEast1 = Region(name: .usEast1) + /// convenience var for US East (Ohio) - public static var usEast2: Region { - return Region(name: RegionName.usEast2) - } - + public static let usEast2 = Region(name: .usEast2) + /// convenience var for US West (N. California) - public static var usWest1: Region { - return Region(name: RegionName.usWest1) - } - + public static let usWest1 = Region(name: .usWest1) + /// convenience var for US West (Oregon) - public static var usWest2: Region { - return Region(name: RegionName.usWest2) - } - + public static let usWest2 = Region(name: .usWest2) + /// convenience var for Canada (Central) - public static var caCentral1: Region { - return Region(name: RegionName.caCentral1) - } - + public static let caCentral1 = Region(name: .caCentral1) + /// convenience var for EU (Frankfurt) - public static var euCentral1: Region { - return Region(name: RegionName.euCentral1) - } - + public static let euCentral1 = Region(name: .euCentral1) + /// convenience var for EU (Ireland) - public static var euWest1: Region { - return Region(name: RegionName.euWest1) - } - + public static let euWest1 = Region(name: .euWest1) + /// convenience var for EU (London) - public static var euWest2: Region { - return Region(name: RegionName.euWest2) - } - + public static let euWest2 = Region(name: .euWest2) + /// convenience var for EU (Paris) - public static var euWest3: Region { - return Region(name: RegionName.euWest3) - } - + public static let euWest3 = Region(name: .euWest3) + /// convenience var for Asia Pacific (Tokyo) - public static var apNortheast1: Region { - return Region(name: RegionName.apNortheast1) - } - + public static let apNortheast1 = Region(name: .apNortheast1) + /// convenience var for Asia Pacific (Seoul) - public static var apNortheast2: Region { - return Region(name: RegionName.apNortheast2) - } - + public static let apNortheast2 = Region(name: .apNortheast2) + /// convenience var for Asia Pacific (Osaka-Local) - public static var apNortheast3: Region { - return Region(name: RegionName.apNortheast3) - } - + public static let apNortheast3 = Region(name: .apNortheast3) + /// convenience var for Asia Pacific (Singapore) - public static var apSoutheast1: Region { - return Region(name: RegionName.apSoutheast1) - } - + public static let apSoutheast1 = Region(name: .apSoutheast1) + /// convenience var for Asia Pacific (Sydney) - public static var apSoutheast2: Region { - return Region(name: RegionName.apSoutheast2) - } - + public static let apSoutheast2 = Region(name: .apSoutheast2) + /// convenience var for Asia Pacific (Mumbai) - public static var apSouth1: Region { - return Region(name: RegionName.apSouth1) - } - + public static let apSouth1 = Region(name: .apSouth1) + /// convenience var for South America (São Paulo) - public static var saEast1: Region { - return Region(name: RegionName.saEast1) - } + public static let saEast1 = Region(name: .saEast1) } /// Codable support for Region extension Region: Codable { - - /// decodes a string (see RegionName) to a Region (does not support custom hosts) + + /// decodes a string (see Name) to a Region (does not support custom hosts) public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - let name = try container.decode(String.self) - - guard let regionName = RegionName(rawValue: name) else { - throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: [], - debugDescription: "Could not find region for \(name)")) - } - - self.name = regionName - self.hostName = nil - self.useTLS = true + try self.init(name: .init(.init(from: decoder))) } - /// encodes the name (see RegionName, does not support custom hosts) + /// encodes the name (see Name, does not support custom hosts) public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.name.rawValue) + try name.description.encode(to: encoder) } } diff --git a/Sources/S3Signer/S3Signer+Private.swift b/Sources/S3Signer/S3Signer+Private.swift index 03bf1dc..48db4bb 100755 --- a/Sources/S3Signer/S3Signer+Private.swift +++ b/Sources/S3Signer/S3Signer+Private.swift @@ -31,7 +31,7 @@ extension S3Signer { func createSignature(_ stringToSign: String, timeStampShort: String, region: Region) throws -> String { let dateKey = try HMAC.SHA256.authenticate(timeStampShort.convertToData(), key: "AWS4\(config.secretKey)".convertToData()) - let dateRegionKey = try HMAC.SHA256.authenticate(region.name.rawValue.convertToData(), key: dateKey) + let dateRegionKey = try HMAC.SHA256.authenticate(region.name.description.convertToData(), key: dateKey) let dateRegionServiceKey = try HMAC.SHA256.authenticate(config.service.convertToData(), key: dateRegionKey) let signingKey = try HMAC.SHA256.authenticate("aws4_request".convertToData(), key: dateRegionServiceKey) let signature = try HMAC.SHA256.authenticate(stringToSign.convertToData(), key: signingKey) @@ -44,10 +44,7 @@ extension S3Signer { } func credentialScope(_ timeStampShort: String, region: Region) -> String { - var arr = [timeStampShort, region.name.rawValue, config.service, "aws4_request"] - if region.name == .none { - arr.remove(at: 1) - } + let arr = [timeStampShort, region.name.description, config.service, "aws4_request"] return arr.joined(separator: "/") }