Skip to content

Commit dffe892

Browse files
authored
Merge pull request #29 from vapor-community/secret-manager
Added support for SecretManager API.
2 parents bc4d436 + 4574ff2 commit dffe892

3 files changed

Lines changed: 170 additions & 0 deletions

File tree

Package.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ let package = Package(
1717
.library(
1818
name: "CloudDatastore",
1919
targets: ["CloudDatastore"]),
20+
.library(
21+
name: "CloudSecretManager",
22+
targets: ["CloudSecretManager"]),
2023
],
2124
dependencies: [
2225
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
@@ -44,5 +47,12 @@ let package = Package(
4447
.product(name: "GoogleCloudDatastore", package: "google-cloud-kit"),
4548
.target(name: "GoogleCloud")
4649
]),
50+
.target(
51+
name: "CloudSecretManager",
52+
dependencies: [
53+
.product(name: "Vapor", package: "vapor"),
54+
.product(name: "GoogleCloudSecretManager", package: "google-cloud-kit"),
55+
.target(name: "GoogleCloud")
56+
]),
4757
]
4858
)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// GoogleCloudSecretManagerAPI.swift
3+
//
4+
//
5+
// Created by Andrew Edwards on 4/29/21.
6+
//
7+
8+
import Vapor
9+
@_exported import SecretManager
10+
@_exported import GoogleCloud
11+
12+
extension Application.GoogleCloudPlatform {
13+
14+
private struct CloudSecretManagerAPIKey: StorageKey {
15+
typealias Value = GoogleCloudSecretManagerAPI
16+
}
17+
18+
private struct CloudSecretManagerConfigurationKey: StorageKey {
19+
typealias Value = GoogleCloudSecretManagerConfiguration
20+
}
21+
22+
private struct CloudSecretManagerHTTPClientKey: StorageKey, LockKey {
23+
typealias Value = HTTPClient
24+
}
25+
26+
public var secretManager: GoogleCloudSecretManagerAPI {
27+
get {
28+
if let existing = self.application.storage[CloudSecretManagerAPIKey.self] {
29+
return existing
30+
} else {
31+
return .init(application: self.application, eventLoop: self.application.eventLoopGroup.next())
32+
}
33+
}
34+
35+
nonmutating set {
36+
self.application.storage[CloudSecretManagerAPIKey.self] = newValue
37+
}
38+
}
39+
40+
public struct GoogleCloudSecretManagerAPI {
41+
public let application: Application
42+
public let eventLoop: EventLoop
43+
44+
/// A client used to interact with the `GoogleCloudSecretManager` API.
45+
public var client: GoogleCloudSecretManagerClient {
46+
do {
47+
let new = try GoogleCloudSecretManagerClient(credentials: self.application.googleCloud.credentials,
48+
config: self.configuration,
49+
httpClient: self.http,
50+
eventLoop: self.eventLoop)
51+
return new
52+
} catch {
53+
fatalError("\(error.localizedDescription)")
54+
}
55+
}
56+
57+
/// The configuration for using `GoogleCloudSecretManager` APIs.
58+
public var configuration: GoogleCloudSecretManagerConfiguration {
59+
get {
60+
if let configuration = application.storage[CloudSecretManagerConfigurationKey.self] {
61+
return configuration
62+
} else {
63+
fatalError("Cloud SecretManager configuration has not been set. Use app.googleCloud.secretManager.configuration = ...")
64+
}
65+
}
66+
set {
67+
if application.storage[CloudSecretManagerConfigurationKey.self] == nil {
68+
application.storage[CloudSecretManagerConfigurationKey.self] = newValue
69+
} else {
70+
fatalError("Attempting to override credentials configuration after being set is not allowed.")
71+
}
72+
}
73+
}
74+
75+
/// Custom `HTTPClient` that ignores unclean SSL shutdown.
76+
public var http: HTTPClient {
77+
if let existing = application.storage[CloudSecretManagerHTTPClientKey.self] {
78+
return existing
79+
} else {
80+
let lock = application.locks.lock(for: CloudSecretManagerHTTPClientKey.self)
81+
lock.lock()
82+
defer { lock.unlock() }
83+
if let existing = application.storage[CloudSecretManagerHTTPClientKey.self] {
84+
return existing
85+
}
86+
let new = HTTPClient(
87+
eventLoopGroupProvider: .shared(application.eventLoopGroup),
88+
configuration: HTTPClient.Configuration(ignoreUncleanSSLShutdown: true)
89+
)
90+
application.storage.set(CloudSecretManagerHTTPClientKey.self, to: new) {
91+
try $0.syncShutdown()
92+
}
93+
return new
94+
}
95+
}
96+
}
97+
}
98+
99+
extension Request {
100+
private struct GoogleCloudSecretManagerKey: StorageKey {
101+
typealias Value = GoogleCloudSecretManagerClient
102+
}
103+
104+
/// A client used to interact with the `GoogleCloudSecretManager` API
105+
public var gcSecretManager: GoogleCloudSecretManagerClient {
106+
if let existing = application.storage[GoogleCloudSecretManagerKey.self] {
107+
return existing.hopped(to: self.eventLoop)
108+
} else {
109+
let new = Application.GoogleCloudPlatform.GoogleCloudSecretManagerAPI(application: self.application, eventLoop: self.eventLoop).client
110+
application.storage[GoogleCloudSecretManagerKey.self] = new
111+
return new
112+
}
113+
}
114+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# GoogleCloudSecretManagerAPI
2+
3+
## Getting Started
4+
If you only need to use the [Google Cloud Secret Manager API](https://cloud.google.com/storage/), then this guide will help you get started.
5+
6+
In your `Package.swift` file, make sure you have the following dependencies and targets
7+
8+
```swift
9+
dependencies: [
10+
//...
11+
.package(url: "https://github.com/vapor-community/google-cloud.git", from: "1.0.0"),
12+
],
13+
targets: [
14+
.target(name: "MyAppName", dependencies: [
15+
//...
16+
.product(name: "CloudSecretManager", package: "google-cloud"),
17+
]),
18+
]
19+
```
20+
21+
Now you can setup the configuration for any GCP API globally via `Application`.
22+
23+
In `configure.swift`
24+
25+
```swift
26+
import CloudSecretManager
27+
28+
app.googleCloud.credentials = try GoogleCloudCredentialsConfiguration(projectId: "myprojectid-12345",
29+
credentialsFile: "~/path/to/service-account.json")
30+
```
31+
Next we setup the CloudSecretManager API configuration (specific to this API).
32+
33+
```swift
34+
app.googleCloud.secretManager.configuration = .default()
35+
```
36+
37+
Now we can start using the GoogleCloudSecretManager API
38+
There's a handy extension on `Request` that you can use to get access to a secret manager client via a property named `gcSecretManager`.
39+
40+
```swift
41+
42+
func getCICDSecrets(_ req: Request) throws -> EventLoopFuture<String> {
43+
req.gcSecretManager.secrets.access("my-secret-name", version: "latest")
44+
.map { $0.payload.decodedData! // returns the base64-decoded value of secret }
45+
}
46+
```

0 commit comments

Comments
 (0)