diff --git a/Sources/Commands/Utilities/PluginDelegate.swift b/Sources/Commands/Utilities/PluginDelegate.swift index f0378edd23b..d62ca0d7ce7 100644 --- a/Sources/Commands/Utilities/PluginDelegate.swift +++ b/Sources/Commands/Utilities/PluginDelegate.swift @@ -322,6 +322,20 @@ final class PluginDelegate: PluginInvocationDelegate { } } + func authorizationInfoRequest( + for url: URL, + completion: @escaping (Result) -> Void + ) { + // Get the authorization info from the authorization provider + DispatchQueue.sharedConcurrent.async { + completion(Result { + try self.swiftTool.getAuthorizationProvider()?.authentication(for: url).map { + .init(username: $0, password: $1) + } + }) + } + } + private func createSymbolGraphForPlugin( forTarget targetName: String, options: PluginInvocationSymbolGraphOptions diff --git a/Sources/PackagePlugin/PackageManagerProxy.swift b/Sources/PackagePlugin/PackageManagerProxy.swift index f7b9bebb321..b3a813ade09 100644 --- a/Sources/PackagePlugin/PackageManagerProxy.swift +++ b/Sources/PackagePlugin/PackageManagerProxy.swift @@ -246,6 +246,27 @@ public struct PackageManager { /// The directory that contains the symbol graph files for the target. public var directoryPath: Path } + + /// Return authorization information for the URL. + // FIXME: add a hashing key + public func getAuthorizationInfo( + for url: String + ) throws -> AuthorizationInfo? { + // Ask the plugin host for authorization information, and wait for a response. + // FIXME: We'll want to make this asynchronous when there is back deployment support for it. + return try sendMessageAndWaitForReply(.authorizationInfoRequest(url: url)) { + guard case .authorizationInfoResponse(let result) = $0 else { return nil } + return result.map{ .init($0) } + } + } + + /// Represents authorization information + public struct AuthorizationInfo { + /// The username + public var username: String + /// The password + public var password: String + } } fileprivate extension PackageManager { @@ -440,3 +461,10 @@ fileprivate extension PackageManager.SymbolGraphResult { self.directoryPath = .init(result.directoryPath) } } + +fileprivate extension PackageManager.AuthorizationInfo { + init(_ result: HostToPluginMessage.AuthorizationInfo) { + self.username = result.username + self.password = result.password + } +} diff --git a/Sources/PackagePlugin/PluginMessages.swift b/Sources/PackagePlugin/PluginMessages.swift index b65307e9af3..c5f86f00956 100644 --- a/Sources/PackagePlugin/PluginMessages.swift +++ b/Sources/PackagePlugin/PluginMessages.swift @@ -246,7 +246,14 @@ enum HostToPluginMessage: Codable { struct SymbolGraphResult: Codable { var directoryPath: String } - + + /// A response to a request for authorization info + case authorizationInfoResponse(result: AuthorizationInfo?) + struct AuthorizationInfo: Codable { + var username: String + var password: String + } + /// A response of an error while trying to complete a request. case errorResponse(error: String) } @@ -324,4 +331,7 @@ enum PluginToHostMessage: Codable { var includeSPI: Bool var emitExtensionBlocks: Bool } + + /// the plugin requesting authorization information + case authorizationInfoRequest(url: String) } diff --git a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift index 2b0829a36ae..33d99af9fea 100644 --- a/Sources/SPMBuildCore/Plugins/PluginInvocation.swift +++ b/Sources/SPMBuildCore/Plugins/PluginInvocation.swift @@ -259,6 +259,23 @@ extension PluginTarget { self.observabilityScope.emit(debug: "couldn't send reply to plugin", underlyingError: error) } } + case .authorizationInfoRequest(let _url): + guard let url = URL(string: _url) else { + throw StringError("Invalid URL: \(_url)") + } + self.invocationDelegate.authorizationInfoRequest(for: url) { + do { + switch $0 { + case .success(let result): + responder(try HostToPluginMessage.authorizationInfoResponse(result: result.map{ .init($0) }).toData()) + case .failure(let error): + responder(try HostToPluginMessage.errorResponse(error: String(describing: error)).toData()) + } + } + catch { + self.observabilityScope.emit(debug: "couldn't send reply to plugin", underlyingError: error) + } + } } } } @@ -702,6 +719,9 @@ public protocol PluginInvocationDelegate { /// Called when a plugin requests that the host computes and returns symbol graph information for a particular target. func pluginRequestedSymbolGraph(forTarget name: String, options: PluginInvocationSymbolGraphOptions, completion: @escaping (Result) -> Void) + + /// Called when a plugin requests authorization information through the PackagePlugin APIs. + func authorizationInfoRequest(for url: URL, completion: @escaping (Result) -> Void) } public struct PluginInvocationSymbolGraphOptions { @@ -721,6 +741,15 @@ public struct PluginInvocationSymbolGraphResult { } } +public struct PluginInvocationAuthorizationInfoResult { + public var username: String + public var password: String + public init(username: String, password: String) { + self.username = username + self.password = password + } +} + public enum PluginInvocationBuildSubset { case all(includingTests: Bool) case product(String) @@ -829,6 +858,9 @@ public extension PluginInvocationDelegate { func pluginRequestedSymbolGraph(forTarget name: String, options: PluginInvocationSymbolGraphOptions, completion: @escaping (Result) -> Void) { DispatchQueue.sharedConcurrent.async { completion(Result.failure(StringError("unimplemented"))) } } + func authorizationInfoRequest(for url: URL, completion: @escaping (Result) -> Void) { + DispatchQueue.sharedConcurrent.async { completion(Result.failure(StringError("unimplemented"))) } + } } fileprivate extension PluginInvocationBuildSubset { @@ -999,6 +1031,13 @@ fileprivate extension HostToPluginMessage.SymbolGraphResult { } } +fileprivate extension HostToPluginMessage.AuthorizationInfo { + init(_ result: PluginInvocationAuthorizationInfoResult) { + self.username = result.username + self.password = result.password + } +} + extension ObservabilityMetadata { public var fileLocation: FileLocation? { get {