diff --git a/Apple/Burrow.xcodeproj/project.pbxproj b/Apple/Burrow.xcodeproj/project.pbxproj index 9897f79..995af28 100644 --- a/Apple/Burrow.xcodeproj/project.pbxproj +++ b/Apple/Burrow.xcodeproj/project.pbxproj @@ -42,8 +42,8 @@ D0D4E5A62C8D9E65007F820A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; }; D0F4FAD32C8DC79C0068730A /* BurrowCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D4E5312C8D996F007F820A /* BurrowCore.framework */; }; D0F7594E2C8DAB6B00126CF3 /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = D078F7E02C8DA375008A8CEC /* GRPC */; }; - D0FA10012D10200100112233 /* burrow.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10032D10200100112233 /* burrow.pb.swift */; }; - D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA10042D10200100112233 /* burrow.grpc.swift */; }; + D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4962C8D921A007F820A /* grpc-swift-config.json */; }; + D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */; }; D0F7597E2C8DB30500126CF3 /* CGRPCZlib in Frameworks */ = {isa = PBXBuildFile; productRef = D0F7597D2C8DB30500126CF3 /* CGRPCZlib */; }; D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D4E4992C8D921A007F820A /* Client.swift */; }; /* End PBXBuildFile section */ @@ -154,6 +154,8 @@ D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; }; D0BF09582C8E6789000D8DEC /* UI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UI.xcconfig; sourceTree = ""; }; D0D4E4952C8D921A007F820A /* burrow.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = burrow.proto; sourceTree = ""; }; + D0D4E4962C8D921A007F820A /* grpc-swift-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "grpc-swift-config.json"; sourceTree = ""; }; + D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "swift-protobuf-config.json"; sourceTree = ""; }; D0D4E4992C8D921A007F820A /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; D0D4E49A2C8D921A007F820A /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; D0D4E49E2C8D921A007F820A /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; @@ -177,8 +179,6 @@ D0D4E58E2C8D9D0A007F820A /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; D0D4E58F2C8D9D0A007F820A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; D0D4E5902C8D9D0A007F820A /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; - D0FA10032D10200100112233 /* burrow.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.pb.swift; sourceTree = ""; }; - D0FA10042D10200100112233 /* burrow.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generated/burrow.grpc.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -317,8 +317,8 @@ isa = PBXGroup; children = ( D0D4E4952C8D921A007F820A /* burrow.proto */, - D0FA10032D10200100112233 /* burrow.pb.swift */, - D0FA10042D10200100112233 /* burrow.grpc.swift */, + D0D4E4962C8D921A007F820A /* grpc-swift-config.json */, + D0D4E4972C8D921A007F820A /* swift-protobuf-config.json */, ); path = Client; sourceTree = ""; @@ -428,6 +428,8 @@ ); dependencies = ( D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */, + D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */, + D0F759602C8DB24400126CF3 /* PBXTargetDependency */, ); name = Core; packageProductDependencies = ( @@ -615,8 +617,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D0FA10012D10200100112233 /* burrow.pb.swift in Sources */, - D0FA10022D10200100112233 /* burrow.grpc.swift in Sources */, + D0F759612C8DB24B00126CF3 /* grpc-swift-config.json in Sources */, + D0F759622C8DB24B00126CF3 /* swift-protobuf-config.json in Sources */, D0F7598D2C8DB3DA00126CF3 /* Client.swift in Sources */, D0D4E56B2C8D9C2F007F820A /* Logging.swift in Sources */, ); @@ -687,6 +689,14 @@ target = D0D4E5302C8D996F007F820A /* Core */; targetProxy = D0F4FAD12C8DC7960068730A /* PBXContainerItemProxy */; }; + D0F7595E2C8DB24400126CF3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */; + }; + D0F759602C8DB24400126CF3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */; + }; D0F7598A2C8DB34200126CF3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; productRef = D0F759892C8DB34200126CF3 /* GRPC */; @@ -911,6 +921,16 @@ package = D0B1D10E2C436152004B7823 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */; productName = AsyncAlgorithms; }; + D0F7595D2C8DB24400126CF3 /* GRPCSwiftPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; + productName = "plugin:GRPCSwiftPlugin"; + }; + D0F7595F2C8DB24400126CF3 /* SwiftProtobufPlugin */ = { + isa = XCSwiftPackageProductDependency; + package = D0D4E4852C8D8F29007F820A /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = "plugin:SwiftProtobufPlugin"; + }; D0F7597D2C8DB30500126CF3 /* CGRPCZlib */ = { isa = XCSwiftPackageProductDependency; package = D0D4E4822C8D8EF6007F820A /* XCRemoteSwiftPackageReference "grpc-swift" */; diff --git a/Apple/Core/Client/Generated/burrow.grpc.swift b/Apple/Core/Client/Generated/burrow.grpc.swift deleted file mode 100644 index d1f848c..0000000 --- a/Apple/Core/Client/Generated/burrow.grpc.swift +++ /dev/null @@ -1,761 +0,0 @@ -// -// DO NOT EDIT. -// swift-format-ignore-file -// -// Generated by the protocol buffer compiler. -// Source: burrow.proto -// -import GRPC -import NIO -import NIOConcurrencyHelpers -import SwiftProtobuf - - -/// Usage: instantiate `Burrow_TunnelClient`, then call methods of this protocol to make API calls. -public protocol Burrow_TunnelClientProtocol: GRPCClient { - var serviceName: String { get } - var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get } - - func tunnelConfiguration( - _ request: Burrow_Empty, - callOptions: CallOptions?, - handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void - ) -> ServerStreamingCall - - func tunnelStart( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> UnaryCall - - func tunnelStop( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> UnaryCall - - func tunnelStatus( - _ request: Burrow_Empty, - callOptions: CallOptions?, - handler: @escaping (Burrow_TunnelStatusResponse) -> Void - ) -> ServerStreamingCall -} - -extension Burrow_TunnelClientProtocol { - public var serviceName: String { - return "burrow.Tunnel" - } - - /// Server streaming call to TunnelConfiguration - /// - /// - Parameters: - /// - request: Request to send to TunnelConfiguration. - /// - callOptions: Call options. - /// - handler: A closure called when each response is received from the server. - /// - Returns: A `ServerStreamingCall` with futures for the metadata and status. - public func tunnelConfiguration( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil, - handler: @escaping (Burrow_TunnelConfigurationResponse) -> Void - ) -> ServerStreamingCall { - return self.makeServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? [], - handler: handler - ) - } - - /// Unary call to TunnelStart - /// - /// - Parameters: - /// - request: Request to send to TunnelStart. - /// - callOptions: Call options. - /// - Returns: A `UnaryCall` with futures for the metadata, status and response. - public func tunnelStart( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> UnaryCall { - return self.makeUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? [] - ) - } - - /// Unary call to TunnelStop - /// - /// - Parameters: - /// - request: Request to send to TunnelStop. - /// - callOptions: Call options. - /// - Returns: A `UnaryCall` with futures for the metadata, status and response. - public func tunnelStop( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> UnaryCall { - return self.makeUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? [] - ) - } - - /// Server streaming call to TunnelStatus - /// - /// - Parameters: - /// - request: Request to send to TunnelStatus. - /// - callOptions: Call options. - /// - handler: A closure called when each response is received from the server. - /// - Returns: A `ServerStreamingCall` with futures for the metadata and status. - public func tunnelStatus( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil, - handler: @escaping (Burrow_TunnelStatusResponse) -> Void - ) -> ServerStreamingCall { - return self.makeServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? [], - handler: handler - ) - } -} - -@available(*, deprecated) -extension Burrow_TunnelClient: @unchecked Sendable {} - -@available(*, deprecated, renamed: "Burrow_TunnelNIOClient") -public final class Burrow_TunnelClient: Burrow_TunnelClientProtocol { - private let lock = Lock() - private var _defaultCallOptions: CallOptions - private var _interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? - public let channel: GRPCChannel - public var defaultCallOptions: CallOptions { - get { self.lock.withLock { return self._defaultCallOptions } } - set { self.lock.withLockVoid { self._defaultCallOptions = newValue } } - } - public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { - get { self.lock.withLock { return self._interceptors } } - set { self.lock.withLockVoid { self._interceptors = newValue } } - } - - /// Creates a client for the burrow.Tunnel service. - /// - /// - Parameters: - /// - channel: `GRPCChannel` to the service host. - /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. - /// - interceptors: A factory providing interceptors for each RPC. - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self._defaultCallOptions = defaultCallOptions - self._interceptors = interceptors - } -} - -public struct Burrow_TunnelNIOClient: Burrow_TunnelClientProtocol { - public var channel: GRPCChannel - public var defaultCallOptions: CallOptions - public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? - - /// Creates a client for the burrow.Tunnel service. - /// - /// - Parameters: - /// - channel: `GRPCChannel` to the service host. - /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. - /// - interceptors: A factory providing interceptors for each RPC. - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self.defaultCallOptions = defaultCallOptions - self.interceptors = interceptors - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -public protocol Burrow_TunnelAsyncClientProtocol: GRPCClient { - static var serviceDescriptor: GRPCServiceDescriptor { get } - var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { get } - - func makeTunnelConfigurationCall( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> GRPCAsyncServerStreamingCall - - func makeTunnelStartCall( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> GRPCAsyncUnaryCall - - func makeTunnelStopCall( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> GRPCAsyncUnaryCall - - func makeTunnelStatusCall( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> GRPCAsyncServerStreamingCall -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension Burrow_TunnelAsyncClientProtocol { - public static var serviceDescriptor: GRPCServiceDescriptor { - return Burrow_TunnelClientMetadata.serviceDescriptor - } - - public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? { - return nil - } - - public func makeTunnelConfigurationCall( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncServerStreamingCall { - return self.makeAsyncServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? [] - ) - } - - public func makeTunnelStartCall( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncUnaryCall { - return self.makeAsyncUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? [] - ) - } - - public func makeTunnelStopCall( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncUnaryCall { - return self.makeAsyncUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? [] - ) - } - - public func makeTunnelStatusCall( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncServerStreamingCall { - return self.makeAsyncServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? [] - ) - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension Burrow_TunnelAsyncClientProtocol { - public func tunnelConfiguration( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncResponseStream { - return self.performAsyncServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelConfiguration.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelConfigurationInterceptors() ?? [] - ) - } - - public func tunnelStart( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) async throws -> Burrow_Empty { - return try await self.performAsyncUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStart.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStartInterceptors() ?? [] - ) - } - - public func tunnelStop( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) async throws -> Burrow_Empty { - return try await self.performAsyncUnaryCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStop.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStopInterceptors() ?? [] - ) - } - - public func tunnelStatus( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncResponseStream { - return self.performAsyncServerStreamingCall( - path: Burrow_TunnelClientMetadata.Methods.tunnelStatus.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeTunnelStatusInterceptors() ?? [] - ) - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -public struct Burrow_TunnelAsyncClient: Burrow_TunnelAsyncClientProtocol { - public var channel: GRPCChannel - public var defaultCallOptions: CallOptions - public var interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? - - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_TunnelClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self.defaultCallOptions = defaultCallOptions - self.interceptors = interceptors - } -} - -public protocol Burrow_TunnelClientInterceptorFactoryProtocol: Sendable { - - /// - Returns: Interceptors to use when invoking 'tunnelConfiguration'. - func makeTunnelConfigurationInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'tunnelStart'. - func makeTunnelStartInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'tunnelStop'. - func makeTunnelStopInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'tunnelStatus'. - func makeTunnelStatusInterceptors() -> [ClientInterceptor] -} - -public enum Burrow_TunnelClientMetadata { - public static let serviceDescriptor = GRPCServiceDescriptor( - name: "Tunnel", - fullName: "burrow.Tunnel", - methods: [ - Burrow_TunnelClientMetadata.Methods.tunnelConfiguration, - Burrow_TunnelClientMetadata.Methods.tunnelStart, - Burrow_TunnelClientMetadata.Methods.tunnelStop, - Burrow_TunnelClientMetadata.Methods.tunnelStatus, - ] - ) - - public enum Methods { - public static let tunnelConfiguration = GRPCMethodDescriptor( - name: "TunnelConfiguration", - path: "/burrow.Tunnel/TunnelConfiguration", - type: GRPCCallType.serverStreaming - ) - - public static let tunnelStart = GRPCMethodDescriptor( - name: "TunnelStart", - path: "/burrow.Tunnel/TunnelStart", - type: GRPCCallType.unary - ) - - public static let tunnelStop = GRPCMethodDescriptor( - name: "TunnelStop", - path: "/burrow.Tunnel/TunnelStop", - type: GRPCCallType.unary - ) - - public static let tunnelStatus = GRPCMethodDescriptor( - name: "TunnelStatus", - path: "/burrow.Tunnel/TunnelStatus", - type: GRPCCallType.serverStreaming - ) - } -} - -/// Usage: instantiate `Burrow_NetworksClient`, then call methods of this protocol to make API calls. -public protocol Burrow_NetworksClientProtocol: GRPCClient { - var serviceName: String { get } - var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get } - - func networkAdd( - _ request: Burrow_Network, - callOptions: CallOptions? - ) -> UnaryCall - - func networkList( - _ request: Burrow_Empty, - callOptions: CallOptions?, - handler: @escaping (Burrow_NetworkListResponse) -> Void - ) -> ServerStreamingCall - - func networkReorder( - _ request: Burrow_NetworkReorderRequest, - callOptions: CallOptions? - ) -> UnaryCall - - func networkDelete( - _ request: Burrow_NetworkDeleteRequest, - callOptions: CallOptions? - ) -> UnaryCall -} - -extension Burrow_NetworksClientProtocol { - public var serviceName: String { - return "burrow.Networks" - } - - /// Unary call to NetworkAdd - /// - /// - Parameters: - /// - request: Request to send to NetworkAdd. - /// - callOptions: Call options. - /// - Returns: A `UnaryCall` with futures for the metadata, status and response. - public func networkAdd( - _ request: Burrow_Network, - callOptions: CallOptions? = nil - ) -> UnaryCall { - return self.makeUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkAdd.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? [] - ) - } - - /// Server streaming call to NetworkList - /// - /// - Parameters: - /// - request: Request to send to NetworkList. - /// - callOptions: Call options. - /// - handler: A closure called when each response is received from the server. - /// - Returns: A `ServerStreamingCall` with futures for the metadata and status. - public func networkList( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil, - handler: @escaping (Burrow_NetworkListResponse) -> Void - ) -> ServerStreamingCall { - return self.makeServerStreamingCall( - path: Burrow_NetworksClientMetadata.Methods.networkList.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkListInterceptors() ?? [], - handler: handler - ) - } - - /// Unary call to NetworkReorder - /// - /// - Parameters: - /// - request: Request to send to NetworkReorder. - /// - callOptions: Call options. - /// - Returns: A `UnaryCall` with futures for the metadata, status and response. - public func networkReorder( - _ request: Burrow_NetworkReorderRequest, - callOptions: CallOptions? = nil - ) -> UnaryCall { - return self.makeUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkReorder.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? [] - ) - } - - /// Unary call to NetworkDelete - /// - /// - Parameters: - /// - request: Request to send to NetworkDelete. - /// - callOptions: Call options. - /// - Returns: A `UnaryCall` with futures for the metadata, status and response. - public func networkDelete( - _ request: Burrow_NetworkDeleteRequest, - callOptions: CallOptions? = nil - ) -> UnaryCall { - return self.makeUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkDelete.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? [] - ) - } -} - -@available(*, deprecated) -extension Burrow_NetworksClient: @unchecked Sendable {} - -@available(*, deprecated, renamed: "Burrow_NetworksNIOClient") -public final class Burrow_NetworksClient: Burrow_NetworksClientProtocol { - private let lock = Lock() - private var _defaultCallOptions: CallOptions - private var _interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? - public let channel: GRPCChannel - public var defaultCallOptions: CallOptions { - get { self.lock.withLock { return self._defaultCallOptions } } - set { self.lock.withLockVoid { self._defaultCallOptions = newValue } } - } - public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { - get { self.lock.withLock { return self._interceptors } } - set { self.lock.withLockVoid { self._interceptors = newValue } } - } - - /// Creates a client for the burrow.Networks service. - /// - /// - Parameters: - /// - channel: `GRPCChannel` to the service host. - /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. - /// - interceptors: A factory providing interceptors for each RPC. - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self._defaultCallOptions = defaultCallOptions - self._interceptors = interceptors - } -} - -public struct Burrow_NetworksNIOClient: Burrow_NetworksClientProtocol { - public var channel: GRPCChannel - public var defaultCallOptions: CallOptions - public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? - - /// Creates a client for the burrow.Networks service. - /// - /// - Parameters: - /// - channel: `GRPCChannel` to the service host. - /// - defaultCallOptions: Options to use for each service call if the user doesn't provide them. - /// - interceptors: A factory providing interceptors for each RPC. - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self.defaultCallOptions = defaultCallOptions - self.interceptors = interceptors - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -public protocol Burrow_NetworksAsyncClientProtocol: GRPCClient { - static var serviceDescriptor: GRPCServiceDescriptor { get } - var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { get } - - func makeNetworkAddCall( - _ request: Burrow_Network, - callOptions: CallOptions? - ) -> GRPCAsyncUnaryCall - - func makeNetworkListCall( - _ request: Burrow_Empty, - callOptions: CallOptions? - ) -> GRPCAsyncServerStreamingCall - - func makeNetworkReorderCall( - _ request: Burrow_NetworkReorderRequest, - callOptions: CallOptions? - ) -> GRPCAsyncUnaryCall - - func makeNetworkDeleteCall( - _ request: Burrow_NetworkDeleteRequest, - callOptions: CallOptions? - ) -> GRPCAsyncUnaryCall -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension Burrow_NetworksAsyncClientProtocol { - public static var serviceDescriptor: GRPCServiceDescriptor { - return Burrow_NetworksClientMetadata.serviceDescriptor - } - - public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? { - return nil - } - - public func makeNetworkAddCall( - _ request: Burrow_Network, - callOptions: CallOptions? = nil - ) -> GRPCAsyncUnaryCall { - return self.makeAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkAdd.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? [] - ) - } - - public func makeNetworkListCall( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncServerStreamingCall { - return self.makeAsyncServerStreamingCall( - path: Burrow_NetworksClientMetadata.Methods.networkList.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkListInterceptors() ?? [] - ) - } - - public func makeNetworkReorderCall( - _ request: Burrow_NetworkReorderRequest, - callOptions: CallOptions? = nil - ) -> GRPCAsyncUnaryCall { - return self.makeAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkReorder.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? [] - ) - } - - public func makeNetworkDeleteCall( - _ request: Burrow_NetworkDeleteRequest, - callOptions: CallOptions? = nil - ) -> GRPCAsyncUnaryCall { - return self.makeAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkDelete.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? [] - ) - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -extension Burrow_NetworksAsyncClientProtocol { - public func networkAdd( - _ request: Burrow_Network, - callOptions: CallOptions? = nil - ) async throws -> Burrow_Empty { - return try await self.performAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkAdd.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkAddInterceptors() ?? [] - ) - } - - public func networkList( - _ request: Burrow_Empty, - callOptions: CallOptions? = nil - ) -> GRPCAsyncResponseStream { - return self.performAsyncServerStreamingCall( - path: Burrow_NetworksClientMetadata.Methods.networkList.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkListInterceptors() ?? [] - ) - } - - public func networkReorder( - _ request: Burrow_NetworkReorderRequest, - callOptions: CallOptions? = nil - ) async throws -> Burrow_Empty { - return try await self.performAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkReorder.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkReorderInterceptors() ?? [] - ) - } - - public func networkDelete( - _ request: Burrow_NetworkDeleteRequest, - callOptions: CallOptions? = nil - ) async throws -> Burrow_Empty { - return try await self.performAsyncUnaryCall( - path: Burrow_NetworksClientMetadata.Methods.networkDelete.path, - request: request, - callOptions: callOptions ?? self.defaultCallOptions, - interceptors: self.interceptors?.makeNetworkDeleteInterceptors() ?? [] - ) - } -} - -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -public struct Burrow_NetworksAsyncClient: Burrow_NetworksAsyncClientProtocol { - public var channel: GRPCChannel - public var defaultCallOptions: CallOptions - public var interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? - - public init( - channel: GRPCChannel, - defaultCallOptions: CallOptions = CallOptions(), - interceptors: Burrow_NetworksClientInterceptorFactoryProtocol? = nil - ) { - self.channel = channel - self.defaultCallOptions = defaultCallOptions - self.interceptors = interceptors - } -} - -public protocol Burrow_NetworksClientInterceptorFactoryProtocol: Sendable { - - /// - Returns: Interceptors to use when invoking 'networkAdd'. - func makeNetworkAddInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'networkList'. - func makeNetworkListInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'networkReorder'. - func makeNetworkReorderInterceptors() -> [ClientInterceptor] - - /// - Returns: Interceptors to use when invoking 'networkDelete'. - func makeNetworkDeleteInterceptors() -> [ClientInterceptor] -} - -public enum Burrow_NetworksClientMetadata { - public static let serviceDescriptor = GRPCServiceDescriptor( - name: "Networks", - fullName: "burrow.Networks", - methods: [ - Burrow_NetworksClientMetadata.Methods.networkAdd, - Burrow_NetworksClientMetadata.Methods.networkList, - Burrow_NetworksClientMetadata.Methods.networkReorder, - Burrow_NetworksClientMetadata.Methods.networkDelete, - ] - ) - - public enum Methods { - public static let networkAdd = GRPCMethodDescriptor( - name: "NetworkAdd", - path: "/burrow.Networks/NetworkAdd", - type: GRPCCallType.unary - ) - - public static let networkList = GRPCMethodDescriptor( - name: "NetworkList", - path: "/burrow.Networks/NetworkList", - type: GRPCCallType.serverStreaming - ) - - public static let networkReorder = GRPCMethodDescriptor( - name: "NetworkReorder", - path: "/burrow.Networks/NetworkReorder", - type: GRPCCallType.unary - ) - - public static let networkDelete = GRPCMethodDescriptor( - name: "NetworkDelete", - path: "/burrow.Networks/NetworkDelete", - type: GRPCCallType.unary - ) - } -} - diff --git a/Apple/Core/Client/Generated/burrow.pb.swift b/Apple/Core/Client/Generated/burrow.pb.swift deleted file mode 100644 index bba0f16..0000000 --- a/Apple/Core/Client/Generated/burrow.pb.swift +++ /dev/null @@ -1,566 +0,0 @@ -// DO NOT EDIT. -// swift-format-ignore-file -// swiftlint:disable all -// -// Generated by the Swift generator plugin for the protocol buffer compiler. -// Source: burrow.proto -// -// For information on using the generated types, please see the documentation: -// https://github.com/apple/swift-protobuf/ - -import Foundation -import SwiftProtobuf - -// If the compiler emits an error on this type, it is because this file -// was generated by a version of the `protoc` Swift plug-in that is -// incompatible with the version of SwiftProtobuf to which you are linking. -// Please ensure that you are building against the same version of the API -// that was used to generate this file. -fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { - struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} - typealias Version = _2 -} - -public enum Burrow_NetworkType: SwiftProtobuf.Enum, Swift.CaseIterable { - public typealias RawValue = Int - case wireGuard // = 0 - case tailnet // = 1 - case UNRECOGNIZED(Int) - - public init() { - self = .wireGuard - } - - public init?(rawValue: Int) { - switch rawValue { - case 0: self = .wireGuard - case 1: self = .tailnet - default: self = .UNRECOGNIZED(rawValue) - } - } - - public var rawValue: Int { - switch self { - case .wireGuard: return 0 - case .tailnet: return 1 - case .UNRECOGNIZED(let i): return i - } - } - - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Burrow_NetworkType] = [ - .wireGuard, - .tailnet, - ] - -} - -public enum Burrow_State: SwiftProtobuf.Enum, Swift.CaseIterable { - public typealias RawValue = Int - case stopped // = 0 - case running // = 1 - case UNRECOGNIZED(Int) - - public init() { - self = .stopped - } - - public init?(rawValue: Int) { - switch rawValue { - case 0: self = .stopped - case 1: self = .running - default: self = .UNRECOGNIZED(rawValue) - } - } - - public var rawValue: Int { - switch self { - case .stopped: return 0 - case .running: return 1 - case .UNRECOGNIZED(let i): return i - } - } - - // The compiler won't synthesize support with the UNRECOGNIZED case. - public static let allCases: [Burrow_State] = [ - .stopped, - .running, - ] - -} - -public struct Burrow_NetworkReorderRequest: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var id: Int32 = 0 - - public var index: Int32 = 0 - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_WireGuardPeer: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var endpoint: String = String() - - public var subnet: [String] = [] - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_WireGuardNetwork: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var address: String = String() - - public var dns: String = String() - - public var peer: [Burrow_WireGuardPeer] = [] - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_NetworkDeleteRequest: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var id: Int32 = 0 - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_Network: @unchecked Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var id: Int32 = 0 - - public var type: Burrow_NetworkType = .wireGuard - - public var payload: Data = Data() - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_NetworkListResponse: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var network: [Burrow_Network] = [] - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_Empty: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -public struct Burrow_TunnelStatusResponse: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var state: Burrow_State = .stopped - - public var start: SwiftProtobuf.Google_Protobuf_Timestamp { - get {return _start ?? SwiftProtobuf.Google_Protobuf_Timestamp()} - set {_start = newValue} - } - /// Returns true if `start` has been explicitly set. - public var hasStart: Bool {return self._start != nil} - /// Clears the value of `start`. Subsequent reads from it will return its default value. - public mutating func clearStart() {self._start = nil} - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} - - fileprivate var _start: SwiftProtobuf.Google_Protobuf_Timestamp? = nil -} - -public struct Burrow_TunnelConfigurationResponse: Sendable { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - public var addresses: [String] = [] - - public var mtu: Int32 = 0 - - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -} - -// MARK: - Code below here is support for the SwiftProtobuf runtime. - -fileprivate let _protobuf_package = "burrow" - -extension Burrow_NetworkType: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "WireGuard"), - 1: .same(proto: "Tailnet"), - ] -} - -extension Burrow_State: SwiftProtobuf._ProtoNameProviding { - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "Stopped"), - 1: .same(proto: "Running"), - ] -} - -extension Burrow_NetworkReorderRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".NetworkReorderRequest" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "index"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }() - case 2: try { try decoder.decodeSingularInt32Field(value: &self.index) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if self.id != 0 { - try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) - } - if self.index != 0 { - try visitor.visitSingularInt32Field(value: self.index, fieldNumber: 2) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_NetworkReorderRequest, rhs: Burrow_NetworkReorderRequest) -> Bool { - if lhs.id != rhs.id {return false} - if lhs.index != rhs.index {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_WireGuardPeer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".WireGuardPeer" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "endpoint"), - 2: .same(proto: "subnet"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.endpoint) }() - case 2: try { try decoder.decodeRepeatedStringField(value: &self.subnet) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if !self.endpoint.isEmpty { - try visitor.visitSingularStringField(value: self.endpoint, fieldNumber: 1) - } - if !self.subnet.isEmpty { - try visitor.visitRepeatedStringField(value: self.subnet, fieldNumber: 2) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_WireGuardPeer, rhs: Burrow_WireGuardPeer) -> Bool { - if lhs.endpoint != rhs.endpoint {return false} - if lhs.subnet != rhs.subnet {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_WireGuardNetwork: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".WireGuardNetwork" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "address"), - 2: .same(proto: "dns"), - 3: .same(proto: "peer"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.address) }() - case 2: try { try decoder.decodeSingularStringField(value: &self.dns) }() - case 3: try { try decoder.decodeRepeatedMessageField(value: &self.peer) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if !self.address.isEmpty { - try visitor.visitSingularStringField(value: self.address, fieldNumber: 1) - } - if !self.dns.isEmpty { - try visitor.visitSingularStringField(value: self.dns, fieldNumber: 2) - } - if !self.peer.isEmpty { - try visitor.visitRepeatedMessageField(value: self.peer, fieldNumber: 3) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_WireGuardNetwork, rhs: Burrow_WireGuardNetwork) -> Bool { - if lhs.address != rhs.address {return false} - if lhs.dns != rhs.dns {return false} - if lhs.peer != rhs.peer {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_NetworkDeleteRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".NetworkDeleteRequest" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if self.id != 0 { - try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_NetworkDeleteRequest, rhs: Burrow_NetworkDeleteRequest) -> Bool { - if lhs.id != rhs.id {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_Network: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".Network" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "type"), - 3: .same(proto: "payload"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularInt32Field(value: &self.id) }() - case 2: try { try decoder.decodeSingularEnumField(value: &self.type) }() - case 3: try { try decoder.decodeSingularBytesField(value: &self.payload) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if self.id != 0 { - try visitor.visitSingularInt32Field(value: self.id, fieldNumber: 1) - } - if self.type != .wireGuard { - try visitor.visitSingularEnumField(value: self.type, fieldNumber: 2) - } - if !self.payload.isEmpty { - try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 3) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_Network, rhs: Burrow_Network) -> Bool { - if lhs.id != rhs.id {return false} - if lhs.type != rhs.type {return false} - if lhs.payload != rhs.payload {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_NetworkListResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".NetworkListResponse" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "network"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeRepeatedMessageField(value: &self.network) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if !self.network.isEmpty { - try visitor.visitRepeatedMessageField(value: self.network, fieldNumber: 1) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_NetworkListResponse, rhs: Burrow_NetworkListResponse) -> Bool { - if lhs.network != rhs.network {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_Empty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".Empty" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap() - - public mutating func decodeMessage(decoder: inout D) throws { - // Load everything into unknown fields - while try decoder.nextFieldNumber() != nil {} - } - - public func traverse(visitor: inout V) throws { - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_Empty, rhs: Burrow_Empty) -> Bool { - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_TunnelStatusResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".TunnelStatusResponse" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "state"), - 2: .same(proto: "start"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularEnumField(value: &self.state) }() - case 2: try { try decoder.decodeSingularMessageField(value: &self._start) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 - if self.state != .stopped { - try visitor.visitSingularEnumField(value: self.state, fieldNumber: 1) - } - try { if let v = self._start { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } }() - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_TunnelStatusResponse, rhs: Burrow_TunnelStatusResponse) -> Bool { - if lhs.state != rhs.state {return false} - if lhs._start != rhs._start {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - -extension Burrow_TunnelConfigurationResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".TunnelConfigurationResponse" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "addresses"), - 2: .same(proto: "mtu"), - ] - - public mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeRepeatedStringField(value: &self.addresses) }() - case 2: try { try decoder.decodeSingularInt32Field(value: &self.mtu) }() - default: break - } - } - } - - public func traverse(visitor: inout V) throws { - if !self.addresses.isEmpty { - try visitor.visitRepeatedStringField(value: self.addresses, fieldNumber: 1) - } - if self.mtu != 0 { - try visitor.visitSingularInt32Field(value: self.mtu, fieldNumber: 2) - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Burrow_TunnelConfigurationResponse, rhs: Burrow_TunnelConfigurationResponse) -> Bool { - if lhs.addresses != rhs.addresses {return false} - if lhs.mtu != rhs.mtu {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} diff --git a/Apple/Core/Client/grpc-swift-config.json b/Apple/Core/Client/grpc-swift-config.json new file mode 100644 index 0000000..2d89698 --- /dev/null +++ b/Apple/Core/Client/grpc-swift-config.json @@ -0,0 +1,11 @@ +{ + "invocations": [ + { + "protoFiles": [ + "burrow.proto", + ], + "server": false, + "visibility": "public" + } + ] +} diff --git a/Apple/Core/Client/swift-protobuf-config.json b/Apple/Core/Client/swift-protobuf-config.json new file mode 100644 index 0000000..87aaec3 --- /dev/null +++ b/Apple/Core/Client/swift-protobuf-config.json @@ -0,0 +1,10 @@ +{ + "invocations": [ + { + "protoFiles": [ + "burrow.proto", + ], + "visibility": "public" + } + ] +} diff --git a/Apple/UI/BurrowView.swift b/Apple/UI/BurrowView.swift index b4fa7d8..835510d 100644 --- a/Apple/UI/BurrowView.swift +++ b/Apple/UI/BurrowView.swift @@ -284,7 +284,6 @@ private struct AccountDraft { var identityName = "" var wireGuardConfig = "" - var discoveryEmail = "" var tailnetProvider: TailnetProvider = .tailscale var authority = "" var tailnet = "" @@ -328,9 +327,6 @@ private struct ConfigurationSheetView: View { @State private var errorMessage: String? @State private var loginSessionID: String? @State private var loginStatus: TailnetLoginStatus? - @State private var discoveryStatus: TailnetDiscoveryResponse? - @State private var discoveryError: String? - @State private var isDiscoveringTailnet = false @State private var authorityProbeStatus: TailnetAuthorityProbeStatus? @State private var authorityProbeError: String? @State private var isProbingAuthority = false @@ -453,9 +449,6 @@ private struct ConfigurationSheetView: View { .onChange(of: draft.authority) { _, _ in resetAuthorityProbe() } - .onChange(of: draft.discoveryEmail) { _, _ in - resetTailnetDiscoveryFeedback() - } .onDisappear { pollingTask?.cancel() webAuthenticationTask?.cancel() @@ -466,37 +459,7 @@ private struct ConfigurationSheetView: View { @ViewBuilder private var tailnetSections: some View { Section("Connection") { - TextField("Email address", text: $draft.discoveryEmail) - .textInputAutocapitalization(.never) - .keyboardType(.emailAddress) - .burrowLoginField() - .autocorrectionDisabled() - - Button { - discoverTailnetAuthority() - } label: { - Label { - Text(isDiscoveringTailnet ? "Finding Server" : "Find Server") - } icon: { - Image(systemName: isDiscoveringTailnet ? "hourglass" : "at.circle") - } - } - .buttonStyle(.borderless) - .disabled(isDiscoveringTailnet || normalizedOptional(draft.discoveryEmail) == nil) - - if let discoveryStatus { - tailnetDiscoveryCard(status: discoveryStatus, failure: nil) - } else if let discoveryError { - tailnetDiscoveryCard(status: nil, failure: discoveryError) - } - - Picker( - "Provider", - selection: Binding( - get: { draft.tailnetProvider }, - set: { applyTailnetProvider($0) } - ) - ) { + Picker("Provider", selection: $draft.tailnetProvider) { ForEach(TailnetProvider.allCases) { provider in Text(provider.title).tag(provider) } @@ -540,14 +503,14 @@ private struct ConfigurationSheetView: View { } Section("Authentication") { - if tailnetUsesWebLogin { + if draft.tailnetProvider.usesWebLogin { tailnetWebLoginCard } else { TextField("Username", text: $draft.username) .burrowLoginField() .autocorrectionDisabled() Picker("Authentication", selection: $draft.authMode) { - ForEach(availableTailnetAuthModes) { mode in + ForEach([AccountAuthMode.none, .password, .preauthKey]) { mode in Text(mode.title).tag(mode) } } @@ -620,7 +583,7 @@ private struct ConfigurationSheetView: View { HStack(spacing: 8) { summaryBadge(draft.tailnetProvider.title) summaryBadge( - tailnetUsesWebLogin ? "Web Sign-In" : draft.authMode.title + draft.tailnetProvider.usesWebLogin ? "Web Sign-In" : draft.authMode.title ) } } @@ -693,7 +656,7 @@ private struct ConfigurationSheetView: View { .foregroundStyle(.secondary) } } else { - Text("Burrow launches the local bridge, then opens the real provider sign-in page in-app.") + Text("Burrow launches the local bridge, then opens the real Tailscale sign-in page in-app.") .font(.footnote) .foregroundStyle(.secondary) } @@ -733,41 +696,6 @@ private struct ConfigurationSheetView: View { ) } - private func tailnetDiscoveryCard( - status: TailnetDiscoveryResponse?, - failure: String? - ) -> some View { - VStack(alignment: .leading, spacing: 6) { - if let status { - Text("Discovered \(status.provider.title)") - .font(.subheadline.weight(.medium)) - Text(status.authority) - .font(.footnote.monospaced()) - .foregroundStyle(.secondary) - .textSelection(.enabled) - if let oidcIssuer = status.oidcIssuer { - Text("OIDC: \(oidcIssuer)") - .font(.footnote) - .foregroundStyle(.secondary) - .lineLimit(3) - .textSelection(.enabled) - } - } else if let failure { - Text("Discovery failed") - .font(.subheadline.weight(.medium)) - .foregroundStyle(.red) - Text(failure) - .font(.footnote) - .foregroundStyle(.secondary) - } - } - .padding(12) - .background( - RoundedRectangle(cornerRadius: 16) - .fill(.thinMaterial) - ) - } - private func summaryBadge(_ label: String) -> some View { Text(label) .font(.caption.weight(.medium)) @@ -834,12 +762,12 @@ private struct ConfigurationSheetView: View { } } - if availableTailnetAuthModes.count > 1 { + if !draft.tailnetProvider.usesWebLogin { Menu("Authentication") { - ForEach(availableTailnetAuthModes) { mode in + ForEach([AccountAuthMode.none, .password, .preauthKey]) { mode in Button(mode.title) { draft.authMode = mode - if mode == .none || mode == .web { + if mode == .none { draft.secret = "" } } @@ -920,7 +848,7 @@ private struct ConfigurationSheetView: View { case .tor: return "Save Account" case .tailnet: - if tailnetUsesWebLogin { + if draft.tailnetProvider.usesWebLogin { return loginStatus?.running == true ? "Save Account" : "Start Sign-In" } return "Save Account" @@ -937,12 +865,12 @@ private struct ConfigurationSheetView: View { if normalizedOptional(draft.accountName) == nil || normalizedOptional(draft.identityName) == nil { return true } + if draft.tailnetProvider.usesWebLogin { + return false + } if draft.tailnetProvider.requiresControlURL && normalizedOptional(draft.authority) == nil { return true } - if tailnetUsesWebLogin { - return false - } if draft.authMode != .none && normalizedOptional(draft.secret) == nil { return true } @@ -1027,14 +955,14 @@ private struct ConfigurationSheetView: View { } private func submitTailnet() async throws { - if tailnetUsesWebLogin { + if draft.tailnetProvider.usesWebLogin { if loginStatus?.running == true { webAuthenticationTask?.cancel() webAuthenticationTask = nil try await saveTailnetAccount(secret: nil, username: nil) dismiss() } else { - try await startTailnetLogin() + try await startTailscaleLogin() } return } @@ -1045,13 +973,13 @@ private struct ConfigurationSheetView: View { dismiss() } - private func startTailnetLogin() async throws { + private func startTailscaleLogin() async throws { let response = try await TailnetBridgeClient.startLogin( TailnetLoginStartRequest( accountName: normalized(draft.accountName, fallback: "default"), identityName: normalized(draft.identityName, fallback: "apple"), hostname: normalizedOptional(draft.hostname), - controlURL: normalizedOptional(draft.authority) ?? draft.tailnetProvider.defaultAuthority + controlURL: draft.tailnetProvider.defaultAuthority ) ) loginSessionID = response.sessionID @@ -1082,7 +1010,7 @@ private struct ConfigurationSheetView: View { case .tailnetLogin: draft.tailnetProvider = .tailscale do { - try await startTailnetLogin() + try await startTailscaleLogin() } catch { errorMessage = error.localizedDescription } @@ -1150,14 +1078,14 @@ private struct ConfigurationSheetView: View { let provider = draft.tailnetProvider let title = titleOrFallback( hostnameFallback( - from: tailnetUsesWebLogin ? (loginStatus?.tailnetName ?? "") : draft.authority, + from: provider.usesWebLogin ? (loginStatus?.tailnetName ?? "") : draft.authority, fallback: provider.title ) ) let payload = TailnetNetworkPayload( provider: provider, - authority: normalizedOptional(draft.authority) ?? normalizedOptional(provider.defaultAuthority ?? ""), + authority: normalizedOptional(provider.defaultAuthority ?? draft.authority), account: normalized(draft.accountName, fallback: "default"), identity: normalized(draft.identityName, fallback: "apple"), tailnet: normalizedOptional(loginStatus?.tailnetName ?? draft.tailnet), @@ -1166,7 +1094,7 @@ private struct ConfigurationSheetView: View { var noteParts: [String] = [ provider.title, - tailnetUsesWebLogin + provider.usesWebLogin ? "State: \(loginStatus?.backendState ?? "NeedsLogin")" : "Auth: \(draft.authMode.title)", ] @@ -1195,7 +1123,7 @@ private struct ConfigurationSheetView: View { hostname: payload.hostname, username: username, tailnet: payload.tailnet, - authMode: tailnetUsesWebLogin ? .web : draft.authMode, + authMode: provider.usesWebLogin ? .web : draft.authMode, note: noteParts.joined(separator: " • "), createdAt: .now, updatedAt: .now @@ -1227,25 +1155,18 @@ private struct ConfigurationSheetView: View { } private func applyTailnetProvider(_ provider: TailnetProvider) { - resetTailnetDiscoveryFeedback() draft.tailnetProvider = provider applyTailnetDefaults(for: provider) } private func applyTailnetDefaults(for provider: TailnetProvider) { draft.authority = provider.defaultAuthority ?? "" - loginStatus = nil - loginSessionID = nil - pollingTask?.cancel() - if provider == .tailscale { + if provider.usesWebLogin { draft.authMode = .web draft.username = "" draft.secret = "" } else { - if !availableTailnetAuthModes.contains(draft.authMode) { - draft.authMode = provider.supportsWebLogin ? .web : .none - } - if draft.authMode == .web && !provider.supportsWebLogin { + if draft.authMode == .web { draft.authMode = .none } } @@ -1281,41 +1202,6 @@ private struct ConfigurationSheetView: View { authorityProbeError = nil } - private func resetTailnetDiscoveryFeedback() { - discoveryStatus = nil - discoveryError = nil - } - - private func discoverTailnetAuthority() { - guard let email = normalizedOptional(draft.discoveryEmail) else { - discoveryStatus = nil - discoveryError = "Enter an email address first." - return - } - - isDiscoveringTailnet = true - discoveryStatus = nil - discoveryError = nil - - Task { @MainActor in - defer { isDiscoveringTailnet = false } - do { - let discovery = try await TailnetDiscoveryClient.discover(email: email) - discoveryStatus = discovery - draft.tailnetProvider = discovery.provider - draft.authority = discovery.authority - if discovery.provider.supportsWebLogin, discovery.oidcIssuer != nil { - draft.authMode = .web - draft.username = "" - draft.secret = "" - } - probeTailnetAuthority() - } catch { - discoveryError = error.localizedDescription - } - } - } - private func pasteWireGuardConfiguration() { guard let clipboardString else { return } draft.wireGuardConfig = clipboardString @@ -1361,21 +1247,6 @@ private struct ConfigurationSheetView: View { return host } - private var tailnetUsesWebLogin: Bool { - draft.authMode == .web && draft.tailnetProvider.supportsWebLogin - } - - private var availableTailnetAuthModes: [AccountAuthMode] { - switch draft.tailnetProvider { - case .tailscale: - [.web] - case .headscale: - [.web, .none, .password, .preauthKey] - case .burrow: - [.none, .password, .preauthKey] - } - } - @ViewBuilder private func labeledValue(_ label: String, _ value: String) -> some View { VStack(alignment: .leading, spacing: 2) { diff --git a/Apple/UI/Networks/Network.swift b/Apple/UI/Networks/Network.swift index 9a534ce..71e5bca 100644 --- a/Apple/UI/Networks/Network.swift +++ b/Apple/UI/Networks/Network.swift @@ -33,13 +33,6 @@ struct TailnetLoginStartRequest: Codable, Sendable { var controlURL: String? } -struct TailnetDiscoveryResponse: Codable, Sendable { - var domain: String - var provider: TailnetProvider - var authority: String - var oidcIssuer: String? -} - struct TailnetLoginStatus: Codable, Sendable { var backendState: String var authURL: String? @@ -98,7 +91,7 @@ enum TailnetBridgeClient { return try decoder.decode(TailnetLoginStatus.self, from: data) } - fileprivate static func validate(response: URLResponse, data: Data) throws { + private static func validate(response: URLResponse, data: Data) throws { guard let http = response as? HTTPURLResponse else { throw URLError(.badServerResponse) } @@ -111,32 +104,6 @@ enum TailnetBridgeClient { } } -enum TailnetDiscoveryClient { - private static let baseURL = URL(string: "http://127.0.0.1:8080")! - - static func discover(email: String) async throws -> TailnetDiscoveryResponse { - guard var components = URLComponents( - url: baseURL.appendingPathComponent("v1/tailnet/discover"), - resolvingAgainstBaseURL: false - ) else { - throw URLError(.badURL) - } - components.queryItems = [ - URLQueryItem(name: "email", value: email) - ] - guard let url = components.url else { - throw URLError(.badURL) - } - - let (data, response) = try await URLSession.shared.data(from: url) - try TailnetBridgeClient.validate(response: response, data: data) - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - return try decoder.decode(TailnetDiscoveryResponse.self, from: data) - } -} - enum TailnetAuthorityProbeClient { static func probe(provider: TailnetProvider, authority: String) async throws -> TailnetAuthorityProbeStatus { let normalizedAuthority = normalizeAuthority(authority) @@ -341,13 +308,8 @@ enum TailnetProvider: String, CaseIterable, Codable, Identifiable, Sendable { } } - var supportsWebLogin: Bool { - switch self { - case .tailscale, .headscale: - true - case .burrow: - false - } + var usesWebLogin: Bool { + self == .tailscale } var requiresControlURL: Bool { @@ -370,7 +332,7 @@ enum TailnetProvider: String, CaseIterable, Codable, Identifiable, Sendable { case .tailscale: "Use Tailscale's real browser login flow." case .headscale: - "Use your Headscale control plane with browser or key-based sign-in." + "Store a Headscale control-plane endpoint and credentials." case .burrow: "Store Burrow control-plane credentials." } diff --git a/burrow/src/auth/server/mod.rs b/burrow/src/auth/server/mod.rs index fdffce3..b0c0522 100644 --- a/burrow/src/auth/server/mod.rs +++ b/burrow/src/auth/server/mod.rs @@ -5,18 +5,17 @@ use std::{env, path::Path}; use anyhow::{Context, Result}; use axum::{ - extract::{Json, Path as AxumPath, Query, State}, + extract::{Json, Path as AxumPath, State}, http::{header::AUTHORIZATION, HeaderMap, StatusCode}, response::IntoResponse, routing::{get, post}, Router, }; -use serde::Deserialize; use tokio::signal; use crate::control::{ - discovery, LocalAuthRequest, LocalAuthResponse, MapRequest, MapResponse, RegisterRequest, - RegisterResponse, TailnetDiscovery, BURROW_TAILNET_DOMAIN, + LocalAuthRequest, LocalAuthResponse, MapRequest, MapResponse, RegisterRequest, + RegisterResponse, BURROW_TAILNET_DOMAIN, }; #[derive(Clone, Debug)] @@ -106,11 +105,6 @@ struct AppState { tailscale: tailscale::TailscaleBridgeManager, } -#[derive(Debug, Deserialize)] -struct TailnetDiscoveryQuery { - email: String, -} - type AppResult = Result; pub async fn serve() -> Result<()> { @@ -145,7 +139,6 @@ pub fn build_router(config: AuthServerConfig) -> Router { .route("/v1/auth/login", post(login_local)) .route("/v1/control/register", post(control_register)) .route("/v1/control/map", post(control_map)) - .route("/v1/tailnet/discover", get(tailnet_discover)) .route("/v1/tailscale/login/start", post(tailscale_login_start)) .route("/v1/tailscale/login/:session_id", get(tailscale_login_status)) .with_state(AppState { @@ -212,19 +205,6 @@ async fn control_map( Ok(Json(response)) } -async fn tailnet_discover( - Query(query): Query, -) -> AppResult> { - if query.email.trim().is_empty() { - return Err((StatusCode::BAD_REQUEST, "email is required".to_owned())); - } - - let discovery = discovery::discover_tailnet(&query.email) - .await - .map_err(|err| (StatusCode::BAD_GATEWAY, err.to_string()))?; - Ok(Json(discovery)) -} - async fn tailscale_login_start( State(state): State, Json(request): Json, @@ -414,17 +394,4 @@ mod tests { assert!(map.dns.expect("dns").magic_dns); Ok(()) } - - #[tokio::test] - async fn tailnet_discover_requires_email() -> Result<()> { - let app = build_router(AuthServerConfig::default()); - let response = app - .oneshot( - Request::get("/v1/tailnet/discover?email=") - .body(Body::empty())?, - ) - .await?; - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - Ok(()) - } } diff --git a/burrow/src/control/discovery.rs b/burrow/src/control/discovery.rs deleted file mode 100644 index 28b48bb..0000000 --- a/burrow/src/control/discovery.rs +++ /dev/null @@ -1,212 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -use reqwest::{Client, StatusCode, Url}; -use serde::{Deserialize, Serialize}; - -use super::TailnetProvider; - -pub const TAILNET_DISCOVERY_REL: &str = "https://burrow.net/rel/tailnet-control-server"; -const TAILNET_DISCOVERY_PATH: &str = "/.well-known/burrow-tailnet"; -const WEBFINGER_PATH: &str = "/.well-known/webfinger"; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct TailnetDiscovery { - pub domain: String, - pub provider: TailnetProvider, - pub authority: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub oidc_issuer: Option, -} - -#[derive(Clone, Debug, Default, Deserialize)] -struct WebFingerDocument { - #[serde(default)] - links: Vec, -} - -#[derive(Clone, Debug, Default, Deserialize)] -struct WebFingerLink { - #[serde(default)] - rel: String, - #[serde(default)] - href: Option, -} - -pub async fn discover_tailnet(email: &str) -> Result { - let domain = email_domain(email)?; - let base_url = Url::parse(&format!("https://{domain}")) - .with_context(|| format!("invalid discovery domain {domain}"))?; - let client = Client::builder() - .user_agent("burrow-tailnet-discovery") - .timeout(std::time::Duration::from_secs(10)) - .build() - .context("failed to build tailnet discovery client")?; - discover_tailnet_at(&client, email, &base_url).await -} - -pub async fn discover_tailnet_at( - client: &Client, - email: &str, - base_url: &Url, -) -> Result { - let domain = email_domain(email)?; - - if let Some(discovery) = discover_well_known(client, base_url).await? { - return Ok(TailnetDiscovery { domain, ..discovery }); - } - - if let Some(authority) = discover_webfinger(client, email, base_url).await? { - return Ok(TailnetDiscovery { - domain, - provider: TailnetProvider::Headscale, - authority, - oidc_issuer: None, - }); - } - - Err(anyhow!("no tailnet discovery metadata found for {domain}")) -} - -pub fn email_domain(email: &str) -> Result { - let trimmed = email.trim(); - let (_, domain) = trimmed - .rsplit_once('@') - .ok_or_else(|| anyhow!("email address must include a domain"))?; - let domain = domain.trim().trim_matches('.').to_ascii_lowercase(); - if domain.is_empty() { - return Err(anyhow!("email address must include a domain")); - } - Ok(domain) -} - -async fn discover_well_known(client: &Client, base_url: &Url) -> Result> { - let url = base_url - .join(TAILNET_DISCOVERY_PATH) - .context("failed to build tailnet discovery URL")?; - let response = client - .get(url) - .header("accept", "application/json") - .send() - .await - .context("tailnet well-known request failed")?; - - match response.status() { - StatusCode::OK => response - .json::() - .await - .context("invalid tailnet discovery document") - .map(Some), - StatusCode::NOT_FOUND => Ok(None), - status => Err(anyhow!("tailnet well-known lookup failed with HTTP {status}")), - } -} - -async fn discover_webfinger(client: &Client, email: &str, base_url: &Url) -> Result> { - let mut url = base_url - .join(WEBFINGER_PATH) - .context("failed to build webfinger URL")?; - url.query_pairs_mut() - .append_pair("resource", &format!("acct:{email}")) - .append_pair("rel", TAILNET_DISCOVERY_REL); - - let response = client - .get(url) - .header("accept", "application/jrd+json, application/json") - .send() - .await - .context("tailnet webfinger request failed")?; - - match response.status() { - StatusCode::OK => { - let document = response - .json::() - .await - .context("invalid webfinger document")?; - Ok(document - .links - .into_iter() - .find(|link| link.rel == TAILNET_DISCOVERY_REL) - .and_then(|link| link.href) - .filter(|href| !href.trim().is_empty())) - } - StatusCode::NOT_FOUND => Ok(None), - status => Err(anyhow!("tailnet webfinger lookup failed with HTTP {status}")), - } -} - -#[cfg(test)] -mod tests { - use axum::{routing::get, Router}; - use serde_json::json; - use tokio::net::TcpListener; - - use super::*; - - #[test] - fn extracts_domain_from_email() { - assert_eq!(email_domain("Contact@Burrow.net").unwrap(), "burrow.net"); - assert!(email_domain("contact").is_err()); - } - - #[tokio::test] - async fn discovers_from_well_known_document() -> Result<()> { - let router = Router::new().route( - TAILNET_DISCOVERY_PATH, - get(|| async { - axum::Json(json!({ - "domain": "burrow.net", - "provider": "headscale", - "authority": "https://ts.burrow.net", - "oidc_issuer": "https://auth.burrow.net/application/o/ts/" - })) - }), - ); - - let listener = TcpListener::bind("127.0.0.1:0").await?; - let base_url = Url::parse(&format!("http://{}", listener.local_addr()?))?; - let server = tokio::spawn(async move { axum::serve(listener, router).await }); - - let client = Client::builder().build()?; - let discovery = discover_tailnet_at(&client, "contact@burrow.net", &base_url).await?; - assert_eq!(discovery.provider, TailnetProvider::Headscale); - assert_eq!(discovery.authority, "https://ts.burrow.net"); - assert_eq!(discovery.domain, "burrow.net"); - - server.abort(); - Ok(()) - } - - #[tokio::test] - async fn falls_back_to_webfinger_authority() -> Result<()> { - let router = Router::new() - .route( - TAILNET_DISCOVERY_PATH, - get(|| async { (StatusCode::NOT_FOUND, "") }), - ) - .route( - WEBFINGER_PATH, - get(|| async { - axum::Json(json!({ - "subject": "acct:contact@burrow.net", - "links": [ - { - "rel": TAILNET_DISCOVERY_REL, - "href": "https://ts.burrow.net" - } - ] - })) - }), - ); - - let listener = TcpListener::bind("127.0.0.1:0").await?; - let base_url = Url::parse(&format!("http://{}", listener.local_addr()?))?; - let server = tokio::spawn(async move { axum::serve(listener, router).await }); - - let client = Client::builder().build()?; - let discovery = discover_tailnet_at(&client, "contact@burrow.net", &base_url).await?; - assert_eq!(discovery.provider, TailnetProvider::Headscale); - assert_eq!(discovery.authority, "https://ts.burrow.net"); - - server.abort(); - Ok(()) - } -} diff --git a/burrow/src/control/mod.rs b/burrow/src/control/mod.rs index 472f673..331a7d2 100644 --- a/burrow/src/control/mod.rs +++ b/burrow/src/control/mod.rs @@ -1,5 +1,4 @@ pub mod config; -pub mod discovery; use std::collections::BTreeMap; @@ -7,7 +6,6 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; pub use config::{TailnetConfig, TailnetProvider}; -pub use discovery::{TailnetDiscovery, TAILNET_DISCOVERY_REL}; pub const BURROW_CAPABILITY_VERSION: i32 = 1; pub const BURROW_TAILNET_DOMAIN: &str = "burrow.net"; diff --git a/nixos/modules/burrow-forge.nix b/nixos/modules/burrow-forge.nix index 0d0f5c8..51af7eb 100644 --- a/nixos/modules/burrow-forge.nix +++ b/nixos/modules/burrow-forge.nix @@ -259,12 +259,9 @@ in encode gzip zstd @oidcConfig path /.well-known/openid-configuration redir @oidcConfig https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/.well-known/openid-configuration 308 - @tailnetConfig path /.well-known/burrow-tailnet - header @tailnetConfig Content-Type application/json - respond @tailnetConfig "{\"domain\":\"${cfg.siteDomain}\",\"provider\":\"headscale\",\"authority\":\"https://${config.services.burrow.headscale.domain}\",\"oidc_issuer\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.headscaleProviderSlug}/\"}" 200 @webfinger path /.well-known/webfinger header @webfinger Content-Type application/jrd+json - respond @webfinger "{\"subject\":\"{query.resource}\",\"links\":[{\"rel\":\"http://openid.net/specs/connect/1.0/issuer\",\"href\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/\"},{\"rel\":\"https://burrow.net/rel/tailnet-control-server\",\"href\":\"https://${config.services.burrow.headscale.domain}\"}]}" 200 + respond @webfinger "{\"subject\":\"{query.resource}\",\"links\":[{\"rel\":\"http://openid.net/specs/connect/1.0/issuer\",\"href\":\"https://${config.services.burrow.authentik.domain}/application/o/${config.services.burrow.authentik.forgejoProviderSlug}/\"}]}" 200 @root path / redir @root ${homeRepoUrl} 308 respond 404