Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IAMServiceAccountCredentials API #33

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// swift-tools-version:5.2
// swift-tools-version:5.7.0

import PackageDescription

let package = Package(
name: "google-cloud",
platforms: [
.macOS(.v10_15)
.macOS(.v13)
],
products: [
.library(
Expand All @@ -20,6 +20,9 @@ let package = Package(
.library(
name: "CloudSecretManager",
targets: ["CloudSecretManager"]),
.library(
name: "CloudIAMServiceAccountCredentials",
targets: ["CloudIAMServiceAccountCredentials"]),
.library(
name: "CloudTranslation",
targets: ["CloudTranslation"]),
Expand All @@ -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", from: "1.0.0-rc.11"),
],
targets: [
.target(
Expand Down Expand Up @@ -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: [
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
52 changes: 52 additions & 0 deletions Sources/CloudIAMServiceAccountCredentials/README.md
Original file line number Diff line number Diff line change
@@ -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<String> {

let unsignedToken: any JWTPayload = Payload()

req.gcIAMServiceAccountCredentials.api.signJWT(
unsignedToken
serviceAccount: "[email protected]"
)
.map { $0.signedJwt }
}
```