Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Add basic template for Secret Manager (#2195)
Browse files Browse the repository at this point in the history
Add template class for interfacing with Secret Manager which supports creating and reading secrets. Contributes to #2176.
  • Loading branch information
dzou committed Feb 28, 2020
1 parent ab68cb1 commit 39c84c8
Show file tree
Hide file tree
Showing 8 changed files with 562 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<module>spring-cloud-gcp-data-firestore</module>
<module>spring-cloud-gcp-bigquery</module>
<module>spring-cloud-gcp-security-firebase</module>
<module>spring-cloud-gcp-secretmanager</module>
</modules>

<properties>
Expand Down
29 changes: 29 additions & 0 deletions spring-cloud-gcp-secretmanager/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-gcp</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>1.2.2.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-gcp-secretmanager</artifactId>
<name>Spring Cloud GCP Secret Manager Module</name>
<description>
Provides Spring Framework integrations with Google Cloud Secret Manager
</description>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-core</artifactId>
</dependency>

<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2017-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.gcp.secretmanager;

import com.google.protobuf.ByteString;

/**
* Describes supported operations that one can perform on the Secret Manager API.
*
* @author Daniel Zou
* @since 1.2.2
*/
public interface SecretManagerOperations {

/**
* Creates a new secret or a new version of existing secret with the provided
* {@code payload}.
*
* <p>
* If there is already a secret saved in SecretManager with the specified
* {@code secretId}, then it simply creates a new version under the secret with the secret
* {@code payload}.
*
* @param secretId the secret ID of the secret to create.
* @param payload the secret payload string.
*/
void createSecret(String secretId, String payload);

/**
* Creates a new secret or a new version of existing secret with the provided
* {@code payload}.
*
* <p>
* If there is already a secret saved in SecretManager with the specified
* {@code secretId}, then it simply creates a new version under the secret with the secret
* {@code payload}.
*
* @param secretId the secret ID of the secret to create.
* @param payload the secret payload as a byte array.
*/
void createSecret(String secretId, byte[] payload);

/**
* Gets the secret payload of the specified {@code secretId} at the latest version.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @return The secret payload as String
*/
String getSecretString(String secretId);

/**
* Gets the secret payload of the specified {@code secretId} at version
* {@code versionName}.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @param versionName which version of the secret to load. The version can be a version
* number as a string (e.g. "5") or an alias (e.g. "latest").
* @return The secret payload as String
*/
String getSecretString(String secretId, String versionName);

/**
* Gets the secret payload of the specified {@code secretId} at the latest version.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @return The secret payload as byte[]
*/
byte[] getSecretBytes(String secretId);

/**
* Gets the secret payload of the specified {@code secretId} at version
* {@code versionName}.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @param versionName which version of the secret to load. The version can be a version
* number as a string (e.g. "5") or an alias (e.g. "latest").
* @return The secret payload as byte[]
*/
byte[] getSecretBytes(String secretId, String versionName);

/**
* Gets the secret payload of the specified {@code secretId} at version
* {@code versionName}.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @param versionName which version of the secret to load. The version can be a version
* number as a string (e.g. "5") or an alias (e.g. "latest").
* @return The secret payload as {@link ByteString}
*/
ByteString getSecretByteString(String secretId, String versionName);

/**
* Returns true if there already exists a secret under the GCP project with the
* {@code secretId}.
*
* @param secretId unique identifier of your secret in Secret Manager.
* @return true if the secret exists in Secret Manager; false otherwise
*/
boolean secretExists(String secretId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright 2017-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.gcp.secretmanager;

import com.google.api.gax.rpc.NotFoundException;
import com.google.cloud.secretmanager.v1beta1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1beta1.AddSecretVersionRequest;
import com.google.cloud.secretmanager.v1beta1.CreateSecretRequest;
import com.google.cloud.secretmanager.v1beta1.ProjectName;
import com.google.cloud.secretmanager.v1beta1.Replication;
import com.google.cloud.secretmanager.v1beta1.Secret;
import com.google.cloud.secretmanager.v1beta1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1beta1.SecretName;
import com.google.cloud.secretmanager.v1beta1.SecretPayload;
import com.google.cloud.secretmanager.v1beta1.SecretVersionName;
import com.google.protobuf.ByteString;

import org.springframework.cloud.gcp.core.GcpProjectIdProvider;

/**
* Offers convenience methods for performing common operations on Secret Manager including
* creating and reading secrets.
*
* @author Daniel Zou
* @since 1.2.2
*/
public class SecretManagerTemplate implements SecretManagerOperations {

private final SecretManagerServiceClient secretManagerServiceClient;

private final GcpProjectIdProvider projectIdProvider;

public SecretManagerTemplate(
SecretManagerServiceClient secretManagerServiceClient,
GcpProjectIdProvider projectIdProvider) {
this.secretManagerServiceClient = secretManagerServiceClient;
this.projectIdProvider = projectIdProvider;
}

@Override
public void createSecret(String secretId, String payload) {
createNewSecretVersion(secretId, ByteString.copyFromUtf8(payload));
}

@Override
public void createSecret(String secretId, byte[] payload) {
createNewSecretVersion(secretId, ByteString.copyFrom(payload));
}

@Override
public String getSecretString(String secretId) {
return getSecretString(secretId, "latest");
}

@Override
public String getSecretString(String secretId, String versionName) {
return getSecretByteString(secretId, versionName).toStringUtf8();
}

@Override
public byte[] getSecretBytes(String secretId) {
return getSecretBytes(secretId, "latest");
}

@Override
public byte[] getSecretBytes(String secretId, String versionName) {
return getSecretByteString(secretId, versionName).toByteArray();
}

@Override
public ByteString getSecretByteString(String secretId, String versionName) {
SecretVersionName secretVersionName = SecretVersionName.of(
this.projectIdProvider.getProjectId(),
secretId,
versionName);

AccessSecretVersionResponse response = secretManagerServiceClient.accessSecretVersion(secretVersionName);

return response.getPayload().getData();
}

@Override
public boolean secretExists(String secretId) {
SecretName secretName = SecretName.of(this.projectIdProvider.getProjectId(), secretId);
try {
this.secretManagerServiceClient.getSecret(secretName);
}
catch (NotFoundException ex) {
return false;
}

return true;
}

/**
* Create a new version of the secret with the specified payload under a {@link Secret}.
* Will also create the parent secret if it does not already exist.
*/
private void createNewSecretVersion(String secretId, ByteString payload) {
if (!secretExists(secretId)) {
createSecret(secretId);
}

SecretName name = SecretName.of(projectIdProvider.getProjectId(), secretId);
AddSecretVersionRequest payloadRequest = AddSecretVersionRequest.newBuilder()
.setParent(name.toString())
.setPayload(SecretPayload.newBuilder().setData(payload))
.build();
secretManagerServiceClient.addSecretVersion(payloadRequest);
}

/**
* Creates a new secret for the GCP Project.
*
* <p>
* Note that the {@link Secret} object does not contain the secret payload. You must
* create versions of the secret which stores the payload of the secret.
*/
private void createSecret(String secretId) {
ProjectName projectName = ProjectName.of(projectIdProvider.getProjectId());

Secret secret = Secret.newBuilder()
.setReplication(
Replication.newBuilder().setAutomatic(
Replication.Automatic.getDefaultInstance()))
.build();
CreateSecretRequest request = CreateSecretRequest.newBuilder()
.setParent(projectName.toString())
.setSecretId(secretId)
.setSecret(secret)
.build();
this.secretManagerServiceClient.createSecret(request);
}
}
Loading

0 comments on commit 39c84c8

Please sign in to comment.