From 5ed7592bd7399b8f0b5dc2d62c89cc47b529fdea Mon Sep 17 00:00:00 2001 From: Owain Hunt Date: Tue, 7 May 2024 16:20:26 +0100 Subject: [PATCH 1/3] Add CloudIAMServiceAccountCredentials target --- Package.swift | 16 ++- ...gleCloudServiceAccountCredentialsAPI.swift | 116 ++++++++++++++++++ .../README.md | 52 ++++++++ 3 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 Sources/CloudIAMServiceAccountCredentials/GoogleCloudServiceAccountCredentialsAPI.swift create mode 100644 Sources/CloudIAMServiceAccountCredentials/README.md diff --git a/Package.swift b/Package.swift index 1fe15a6..9aa7c5d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.10.0 import PackageDescription let package = Package( name: "google-cloud", platforms: [ - .macOS(.v10_15) + .macOS(.v13) ], products: [ .library( @@ -20,6 +20,9 @@ let package = Package( .library( name: "CloudSecretManager", targets: ["CloudSecretManager"]), + .library( + name: "CloudIAMServiceAccountCredentials", + targets: ["CloudIAMServiceAccountCredentials"]), .library( name: "CloudTranslation", targets: ["CloudTranslation"]), @@ -29,7 +32,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), - .package(url: "https://github.com/vapor-community/google-cloud-kit.git", .exact("1.0.0-rc.9")), + .package(url: "https://github.com/vapor-community/google-cloud-kit.git", branch: "main"), ], targets: [ .target( @@ -60,6 +63,13 @@ let package = Package( .product(name: "GoogleCloudSecretManager", package: "google-cloud-kit"), .target(name: "GoogleCloud") ]), + .target( + name: "CloudIAMServiceAccountCredentials", + dependencies: [ + .product(name: "Vapor", package: "vapor"), + .product(name: "GoogleCloudIAMServiceAccountCredentials", package: "google-cloud-kit"), + .target(name: "GoogleCloud") + ]), .target( name: "CloudTranslation", dependencies: [ diff --git a/Sources/CloudIAMServiceAccountCredentials/GoogleCloudServiceAccountCredentialsAPI.swift b/Sources/CloudIAMServiceAccountCredentials/GoogleCloudServiceAccountCredentialsAPI.swift new file mode 100644 index 0000000..bbeb2e3 --- /dev/null +++ b/Sources/CloudIAMServiceAccountCredentials/GoogleCloudServiceAccountCredentialsAPI.swift @@ -0,0 +1,116 @@ +import Vapor +@_exported import IAMServiceAccountCredentials +@_exported import GoogleCloud + +extension Application.GoogleCloudPlatform { + + private struct APIKey: StorageKey { + typealias Value = ServiceAccountCredentialsAPI + } + + private struct ConfigurationKey: StorageKey { + typealias Value = IAMServiceAccountCredentialsConfiguration + } + + private struct HTTPClientKey: StorageKey, LockKey { + typealias Value = HTTPClient + } + + public var iamServiceAccountCredentials: ServiceAccountCredentialsAPI { + get { + if let existing = self.application.storage[APIKey.self] { + return existing + } else { + return .init(application: self.application, eventLoop: self.application.eventLoopGroup.next()) + } + } + + nonmutating set { + self.application.storage[APIKey.self] = newValue + } + } + + public struct ServiceAccountCredentialsAPI { + public let application: Application + public let eventLoop: EventLoop + + /// A client used to interact with the `GoogleCloudIAMServiceAccountCredentials` API. + public var client: IAMServiceAccountCredentialsClient { + do { + let new = try IAMServiceAccountCredentialsClient( + credentials: self.application.googleCloud.credentials, + config: self.configuration, + httpClient: self.http, + eventLoop: self.eventLoop + ) + return new + } catch { + fatalError("\(error.localizedDescription)") + } + } + + /// The configuration for using `GoogleCloudIAMServiceAccountCredentials` APIs. + public var configuration: IAMServiceAccountCredentialsConfiguration { + get { + if let configuration = application.storage[ConfigurationKey.self] { + return configuration + } else { + fatalError("Service Account Credentials configuration has not been set. Use app.googleCloud.iamServiceAccountCredentials.configuration = ...") + } + } + set { + if application.storage[ConfigurationKey.self] == nil { + application.storage[ConfigurationKey.self] = newValue + } else { + fatalError("Attempting to override credentials configuration after being set is not allowed.") + } + } + } + + /// Custom `HTTPClient` that ignores unclean SSL shutdown. + public var http: HTTPClient { + if let existing = application.storage[HTTPClientKey.self] { + return existing + } else { + let lock = application.locks.lock(for: HTTPClientKey.self) + lock.lock() + defer { lock.unlock() } + if let existing = application.storage[HTTPClientKey.self] { + return existing + } + let new = HTTPClient( + eventLoopGroupProvider: .shared(application.eventLoopGroup), + configuration: HTTPClient.Configuration(ignoreUncleanSSLShutdown: true) + ) + application.storage.set(HTTPClientKey.self, to: new) { + try $0.syncShutdown() + } + return new + } + } + } +} + +extension Request { + private struct IAMServiceAccountCredentialsClientKey: StorageKey { + typealias Value = IAMServiceAccountCredentialsClient + } + + /// A client used to interact with the `GoogleCloudIAMServiceAccountCredentials` API + public var gcIAMServiceAccountCredentials: IAMServiceAccountCredentialsClient { + + if let existing = application.storage[IAMServiceAccountCredentialsClientKey.self] { + return existing.hopped(to: self.eventLoop) + } else { + + let new = Application.GoogleCloudPlatform.ServiceAccountCredentialsAPI( + application: self.application, + eventLoop: self.eventLoop + ).client + + application.storage[IAMServiceAccountCredentialsClientKey.self] = new + + return new + } + } +} diff --git a/Sources/CloudIAMServiceAccountCredentials/README.md b/Sources/CloudIAMServiceAccountCredentials/README.md new file mode 100644 index 0000000..99fc786 --- /dev/null +++ b/Sources/CloudIAMServiceAccountCredentials/README.md @@ -0,0 +1,52 @@ +# GoogleCloudIAMServiceAccountCredentialsAPI + +## Getting Started +If you only need to use the [Google Cloud IAM Service Account Credentials API](https://cloud.google.com/iam/docs/reference/credentials/rest), then this guide will help you get started. + +In your `Package.swift` file, make sure you have the following dependencies and targets + +```swift +dependencies: [ + //... + .package(url: "https://github.com/vapor-community/google-cloud.git", from: "1.0.0"), + ], + targets: [ + .target(name: "MyAppName", dependencies: [ + //... + .product(name: "CloudIAMServiceAccountCredentials", package: "google-cloud"), + ]), + ] +``` + +Now you can setup the configuration for any GCP API globally via `Application`. + +In `configure.swift` + +```swift + import CloudIAMServiceAccountCredentials + + app.googleCloud.credentials = try GoogleCloudCredentialsConfiguration(projectId: "myprojectid-12345", + credentialsFile: "~/path/to/service-account.json") +``` +Next we setup the CloudIAMServiceAccountCredentials API configuration (specific to this API). + +```swift +app.googleCloud.iamServiceAccountCredentials.configuration = .default() +``` + +Now we can start using the GoogleCloudIAMServiceAccountCredentials API +There's a handy extension on `Request` that you can use to get access to a secret manager client via a property named `gcIAMServiceAccountCredentials`. + +```swift + +func signJWT(_ req: Request) throws -> EventLoopFuture { + + let unsignedToken: any JWTPayload = Payload() + + req.gcIAMServiceAccountCredentials.api.signJWT( + unsignedToken + serviceAccount: "email-for-my-service@account.com" + ) + .map { $0.signedJwt } +} +``` From 4d1579acd7e2ff2fa912ab4e2cb4a3d778c7cfdf Mon Sep 17 00:00:00 2001 From: Owain Hunt Date: Tue, 7 May 2024 17:00:39 +0100 Subject: [PATCH 2/3] Point to specific google-cloud-kit commit --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 9aa7c5d..17fa7b7 100644 --- a/Package.swift +++ b/Package.swift @@ -32,7 +32,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), - .package(url: "https://github.com/vapor-community/google-cloud-kit.git", branch: "main"), + .package(url: "https://github.com/vapor-community/google-cloud-kit.git", revision: "aba24232e352d11ac76b8ae33e2f770bea2b4fc9"), ], targets: [ .target( From a96470d2e9456187cb35d564e17e4a4cb690c159 Mon Sep 17 00:00:00 2001 From: Owain Hunt Date: Tue, 18 Jun 2024 17:13:08 +0100 Subject: [PATCH 3/3] Point to new google-cloud-kit release and match swift-tools-version --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 17fa7b7..6a10203 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.10.0 +// swift-tools-version:5.7.0 import PackageDescription @@ -32,7 +32,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), - .package(url: "https://github.com/vapor-community/google-cloud-kit.git", revision: "aba24232e352d11ac76b8ae33e2f770bea2b4fc9"), + .package(url: "https://github.com/vapor-community/google-cloud-kit.git", from: "1.0.0-rc.11"), ], targets: [ .target(