Split endpoint and credentials
Basically drop AuthenticatedEndpoint.
This commit is contained in:
parent
40b733db57
commit
98c5a015f3
|
@ -105,41 +105,6 @@ extension TunnelKitProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/// Encapsulates an endpoint along with the authentication credentials.
|
||||
public struct AuthenticatedEndpoint {
|
||||
|
||||
/// The remote hostname or IP address.
|
||||
public let hostname: String
|
||||
|
||||
/// The username.
|
||||
public let username: String?
|
||||
|
||||
/// The password.
|
||||
public let password: String?
|
||||
|
||||
/// :nodoc:
|
||||
public init(hostname: String, username: String, password: String) {
|
||||
self.hostname = hostname
|
||||
self.username = username
|
||||
self.password = password
|
||||
}
|
||||
|
||||
init(protocolConfiguration: NEVPNProtocol) throws {
|
||||
guard let hostname = protocolConfiguration.serverAddress else {
|
||||
throw ProviderError.configuration(field: "protocolConfiguration.serverAddress")
|
||||
}
|
||||
|
||||
self.hostname = hostname
|
||||
if let username = protocolConfiguration.username, let passwordReference = protocolConfiguration.passwordReference {
|
||||
self.username = username
|
||||
password = try? Keychain.password(for: username, reference: passwordReference)
|
||||
} else {
|
||||
username = nil
|
||||
password = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The way to create a `TunnelKitProvider.Configuration` object for the tunnel profile.
|
||||
public struct ConfigurationBuilder {
|
||||
|
||||
|
@ -477,16 +442,17 @@ extension TunnelKitProvider {
|
|||
|
||||
- Parameter bundleIdentifier: The provider bundle identifier required to locate the tunnel extension.
|
||||
- Parameter appGroup: The name of the app group in which the tunnel extension lives in.
|
||||
- Parameter endpoint: The `TunnelKitProvider.AuthenticatedEndpoint` the tunnel will connect to.
|
||||
- Parameter hostname: The hostname the tunnel will connect to.
|
||||
- Parameter credentials: The optional credentials to authenticate with.
|
||||
- Returns: The generated `NETunnelProviderProtocol` object.
|
||||
- Throws: `ProviderError.configuration` if unable to store the `endpoint.password` to the `appGroup` keychain.
|
||||
- Throws: `ProviderError.credentials` if unable to store `credentials.password` to the `appGroup` keychain.
|
||||
*/
|
||||
public func generatedTunnelProtocol(withBundleIdentifier bundleIdentifier: String, appGroup: String, endpoint: AuthenticatedEndpoint) throws -> NETunnelProviderProtocol {
|
||||
public func generatedTunnelProtocol(withBundleIdentifier bundleIdentifier: String, appGroup: String, hostname: String, credentials: SessionProxy.Credentials? = nil) throws -> NETunnelProviderProtocol {
|
||||
let protocolConfiguration = NETunnelProviderProtocol()
|
||||
|
||||
protocolConfiguration.providerBundleIdentifier = bundleIdentifier
|
||||
protocolConfiguration.serverAddress = endpoint.hostname
|
||||
if let username = endpoint.username, let password = endpoint.password {
|
||||
protocolConfiguration.serverAddress = hostname
|
||||
if let username = credentials?.username, let password = credentials?.password {
|
||||
let keychain = Keychain(group: appGroup)
|
||||
do {
|
||||
try keychain.set(password: password, for: username, label: Bundle.main.bundleIdentifier)
|
||||
|
|
|
@ -116,24 +116,26 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
|||
|
||||
/// :nodoc:
|
||||
open override func startTunnel(options: [String : NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) {
|
||||
let endpoint: AuthenticatedEndpoint
|
||||
|
||||
// required configuration
|
||||
let hostname: String
|
||||
do {
|
||||
guard let tunnelProtocol = protocolConfiguration as? NETunnelProviderProtocol else {
|
||||
throw ProviderError.configuration(field: "protocolConfiguration")
|
||||
}
|
||||
guard let serverAddress = tunnelProtocol.serverAddress else {
|
||||
throw ProviderError.configuration(field: "protocolConfiguration.serverAddress")
|
||||
}
|
||||
guard let providerConfiguration = tunnelProtocol.providerConfiguration else {
|
||||
throw ProviderError.configuration(field: "protocolConfiguration.providerConfiguration")
|
||||
}
|
||||
try endpoint = AuthenticatedEndpoint(protocolConfiguration: tunnelProtocol)
|
||||
hostname = serverAddress
|
||||
try appGroup = Configuration.appGroup(from: providerConfiguration)
|
||||
try cfg = Configuration.parsed(from: providerConfiguration)
|
||||
} catch let e {
|
||||
var message: String?
|
||||
if let te = e as? ProviderError {
|
||||
switch te {
|
||||
case .credentials(let field):
|
||||
message = "Tunnel credentials unavailable: \(field)"
|
||||
|
||||
case .configuration(let field):
|
||||
message = "Tunnel configuration incomplete: \(field)"
|
||||
|
||||
|
@ -146,7 +148,16 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
|||
return
|
||||
}
|
||||
|
||||
strategy = ConnectionStrategy(hostname: endpoint.hostname, configuration: cfg)
|
||||
// optional credentials
|
||||
let credentials: SessionProxy.Credentials?
|
||||
if let username = protocolConfiguration.username, let passwordReference = protocolConfiguration.passwordReference,
|
||||
let password = try? Keychain.password(for: username, reference: passwordReference) {
|
||||
credentials = SessionProxy.Credentials(username, password)
|
||||
} else {
|
||||
credentials = nil
|
||||
}
|
||||
|
||||
strategy = ConnectionStrategy(hostname: hostname, configuration: cfg)
|
||||
|
||||
if let defaults = defaults, var existingLog = cfg.existingLog(in: defaults) {
|
||||
if let i = existingLog.index(of: logSeparator) {
|
||||
|
@ -211,8 +222,7 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
|||
|
||||
// log.info("Temporary CA is stored to: \(caPath)")
|
||||
var sessionConfiguration = SessionProxy.ConfigurationBuilder(caPath: caPath)
|
||||
sessionConfiguration.username = endpoint.username
|
||||
sessionConfiguration.password = endpoint.password
|
||||
sessionConfiguration.credentials = credentials
|
||||
sessionConfiguration.cipher = cfg.cipher
|
||||
sessionConfiguration.digest = cfg.digest
|
||||
sessionConfiguration.clientCertificatePath = clientCertificatePath
|
||||
|
|
|
@ -39,6 +39,29 @@ import Foundation
|
|||
|
||||
extension SessionProxy {
|
||||
|
||||
/// A pair of credentials for authentication.
|
||||
public struct Credentials: Codable, Equatable {
|
||||
|
||||
/// The username.
|
||||
public let username: String
|
||||
|
||||
/// The password.
|
||||
public let password: String
|
||||
|
||||
/// :nodoc
|
||||
public init(_ username: String, _ password: String) {
|
||||
self.username = username
|
||||
self.password = password
|
||||
}
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
/// :nodoc:
|
||||
public static func ==(lhs: Credentials, rhs: Credentials) -> Bool {
|
||||
return (lhs.username == rhs.username) && (lhs.password == rhs.password)
|
||||
}
|
||||
}
|
||||
|
||||
/// The available encryption algorithms.
|
||||
public enum Cipher: String, Codable, CustomStringConvertible {
|
||||
|
||||
|
@ -112,11 +135,8 @@ extension SessionProxy {
|
|||
/// The way to create a `SessionProxy.Configuration` object for a `SessionProxy`.
|
||||
public struct ConfigurationBuilder {
|
||||
|
||||
/// An username.
|
||||
public var username: String?
|
||||
|
||||
/// A password.
|
||||
public var password: String?
|
||||
/// The credentials.
|
||||
public var credentials: Credentials?
|
||||
|
||||
/// The cipher algorithm for data encryption.
|
||||
public var cipher: Cipher
|
||||
|
@ -144,8 +164,7 @@ extension SessionProxy {
|
|||
|
||||
/// :nodoc:
|
||||
public init(caPath: String) {
|
||||
username = nil
|
||||
password = nil
|
||||
credentials = nil
|
||||
cipher = .aes128cbc
|
||||
digest = .sha1
|
||||
self.caPath = caPath
|
||||
|
@ -163,8 +182,7 @@ extension SessionProxy {
|
|||
*/
|
||||
public func build() -> Configuration {
|
||||
return Configuration(
|
||||
username: username,
|
||||
password: password,
|
||||
credentials: credentials,
|
||||
cipher: cipher,
|
||||
digest: digest,
|
||||
caPath: caPath,
|
||||
|
@ -180,11 +198,8 @@ extension SessionProxy {
|
|||
/// The immutable configuration for `SessionProxy`.
|
||||
public struct Configuration: Codable {
|
||||
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.username`
|
||||
public let username: String?
|
||||
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.password`
|
||||
public let password: String?
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.credentials`
|
||||
public let credentials: Credentials?
|
||||
|
||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.cipher`
|
||||
public let cipher: Cipher
|
||||
|
|
|
@ -583,7 +583,7 @@ public class SessionProxy {
|
|||
negotiationKey.controlState = .preAuth
|
||||
|
||||
do {
|
||||
authenticator = try Authenticator(configuration.username, pushReply?.authToken ?? configuration.password)
|
||||
authenticator = try Authenticator(configuration.credentials?.username, pushReply?.authToken ?? configuration.credentials?.password)
|
||||
try authenticator?.putAuth(into: negotiationKey.tls)
|
||||
} catch let e {
|
||||
deferStop(.shutdown, e)
|
||||
|
|
|
@ -57,11 +57,8 @@ class AppExtensionTests: XCTestCase {
|
|||
|
||||
let identifier = "com.example.Provider"
|
||||
let appGroup = "group.com.algoritmico.TunnelKit"
|
||||
let endpoint = TunnelKitProvider.AuthenticatedEndpoint(
|
||||
hostname: "example.com",
|
||||
username: "foo",
|
||||
password: "bar"
|
||||
)
|
||||
let hostname = "example.com"
|
||||
let credentials = SessionProxy.Credentials("foo", "bar")
|
||||
|
||||
builder = TunnelKitProvider.ConfigurationBuilder(ca: CryptoContainer(pem: "abcdef"))
|
||||
XCTAssertNotNil(builder)
|
||||
|
@ -70,13 +67,13 @@ class AppExtensionTests: XCTestCase {
|
|||
builder.digest = .sha256
|
||||
cfg = builder.build()
|
||||
|
||||
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, endpoint: endpoint)
|
||||
let proto = try? cfg.generatedTunnelProtocol(withBundleIdentifier: identifier, appGroup: appGroup, hostname: hostname, credentials: credentials)
|
||||
XCTAssertNotNil(proto)
|
||||
|
||||
XCTAssertEqual(proto?.providerBundleIdentifier, identifier)
|
||||
XCTAssertEqual(proto?.serverAddress, endpoint.hostname)
|
||||
XCTAssertEqual(proto?.username, endpoint.username)
|
||||
XCTAssertEqual(proto?.passwordReference, try? Keychain(group: appGroup).passwordReference(for: endpoint.username))
|
||||
XCTAssertEqual(proto?.serverAddress, hostname)
|
||||
XCTAssertEqual(proto?.username, credentials.username)
|
||||
XCTAssertEqual(proto?.passwordReference, try? Keychain(group: appGroup).passwordReference(for: credentials.username))
|
||||
|
||||
if let pc = proto?.providerConfiguration {
|
||||
print("\(pc)")
|
||||
|
|
Loading…
Reference in New Issue