Milesoft Secrets Extension (secrets-ext)
1. Overview & Purpose
The Secrets Extension (secrets-ext) is a unified, highly optimized, and robust developer utility designed to provide a secure and standardized facade around Google Cloud Secret Manager. It simplifies secret management within the Milesoft ecosystem by offering a simple API to load and save encrypted variables, while natively supporting in-memory mock overrides to facilitate seamless local testing and development.
Key Benefits & Core Capabilities:
- Standardized Facade: Provides a clean, type-safe API for loading and saving secrets, shielding downstream developers from raw Google Cloud API boilerplate.
- Automatic Resource Provisioning: Automatically provisions secrets with Automatic replication inside GCP if they do not already exist when a write operation is initiated.
- Zero-Dependency Testing & Overrides: Built-in support for in-memory secret overrides (
OVERRIDESregistry), allowing developers to completely simulate Secret Manager behaviors during local runs or unit/integration testing without GCP dependencies or credentials. - Seamless Spring Boot Configuration Integration: Standardizes how microservices and applications retrieve encrypted external properties during startup through unified base configuration classes.
2. Installation & Dependency Configuration
The Secrets Extension is hosted securely inside our Google Cloud Artifact Registry repository.
To configure your application to resolve and pull this dependency:
2.1 Gradle Setup (build.gradle)
Apply the Google Cloud Artifact Registry plugin and configure the Maven repository block:
plugins {
id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.5"
id "java"
}
repositories {
mavenLocal()
mavenCentral()
maven {
url "artifactregistry://us-west2-maven.pkg.dev/milesoft-repo/repo-maven"
}
}
dependencies {
// Milesoft Commons foundation (required by secrets-ext)
implementation "io.milesoft:commons:2.0.1"
// Secrets Integration Extension
implementation "io.milesoft:secrets-ext:2.0.1"
}
3. Configuring Secrets Management in Spring Boot
Downstream Spring Boot microservices and applications utilize a standard configuration pattern to initialize and inject the SecretManagerService as a Spring-managed bean.
3.1 Define Application Properties
Configure your target GCP project ID in your properties configuration (e.g., application.yml or application.properties):
secrets:
project: "your-gcp-project"
3.2 Extending Secrets Configuration
To enable the SecretManagerService bean inside your application context, extend the standard AbstractSecretsConfig class (provided by our core platform stack framework) and decorate it with Spring's @Configuration annotation:
import io.milesoft.stack.config.AbstractSecretsConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SecretsConfig extends AbstractSecretsConfig {
@Autowired
public SecretsConfig(@Value("${secrets.project}") String project) {
super(project);
}
}
4. Key Components
The library is designed for ultimate stability and testability, exposing several key types for direct runtime usage or custom mocking configurations.
4.1 SecretManagerService
The main Orchestrator service. It manages caching, local overrides, and communicates with Google Cloud's API using standard client abstractions.
4.2 SecretManagerClient Interface
A testing-friendly abstraction that wraps Google's API calls.
Because GCP's underlying SecretManagerServiceClient concrete class consists entirely of final methods (making traditional subclass mocking and stubbing impossible under standard Java test frameworks like Mockito), secrets-ext introduces this custom interface to facilitate 100% test coverage and verification.
4.3 DelegateSecretManagerClient
A standard implementation of SecretManagerClient that acts as a decorator, routing calls directly to the concrete instance of Google Cloud's official client.
5. API Reference Guide
5.1 SecretManagerService
A thread-safe, high-performance orchestration service that handles secret retrieval and writing.
Constructor:
public SecretManagerService(String projectId)
projectId: The GCP Project ID to load secrets from. Must not be blank.
Core Methods:
| Method Signature | Return Type | Description |
|---|---|---|
loadSecret(String key) |
String |
Resolves a secret's value. First checks the static OVERRIDES registry. If absent, connects to GCP, accesses the latest version, and retrieves the UTF-8 payload. Throws IllegalStateException on error. |
saveSecret(String key, String value) |
void |
Writes or updates a secret key. If the secret key doesn't exist, it creates the secret with Automatic replication first, then appends the new value as the latest secret version. |
setOverride(String key, String value) |
void |
Static. Registers an in-memory key-value override. If set, any calls to loadSecret(key) return this override instantly, completely bypassing external network calls. |
clearOverrides() |
void |
Static. Resets and clears the OVERRIDES registry. Recommended for cleanup in teardown test methods. |
6. Practical Real-World Code Examples
These integration examples represent standard, battle-tested patterns for secret retrieval and local/integration testing across the Milesoft codebase.
Example A: Retrieving Secrets inside a Spring @Configuration Class
This example shows how to load third-party API credentials securely from Secret Manager during Spring context initialization and inject them into custom service beans.
import static com.google.common.base.Preconditions.checkNotNull;
import io.milesoft.accounts.services.TwilioService;
import io.milesoft.secrets.services.SecretManagerService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TwilioConfig {
private final String secretAccountSid;
private final String secretAccountToken;
private final String secretServiceSid;
public TwilioConfig(
@Value("${twilio.secretAccountSid}") String secretAccountSid,
@Value("${twilio.secretAccountToken}") String secretAccountToken,
@Value("${twilio.secretServiceSid}") String secretServiceSid) {
this.secretAccountSid = secretAccountSid;
this.secretAccountToken = secretAccountToken;
this.secretServiceSid = secretServiceSid;
}
@Bean
public TwilioService twilioService(
SecretManagerService secretManagerService,
@Value("${twilio.phoneNumber}") String phone) {
checkNotNull(secretManagerService, "secretManagerService must not be null");
// Load the credentials directly using the secret keys
final String accountSid = secretManagerService.loadSecret(secretAccountSid);
final String accountToken = secretManagerService.loadSecret(secretAccountToken);
final String serviceSid = secretManagerService.loadSecret(secretServiceSid);
return new TwilioService(accountSid, accountToken, serviceSid, phone);
}
}
Example B: Secure Test Configuration with In-Memory Overrides
This example demonstrates configuring dummy secret values in a Spring Boot test suite using the setOverride pattern, ensuring that developers and CI/CD pipelines can run automated integration tests without needing actual Google Cloud credentials or billing accounts.
import io.milesoft.secrets.services.SecretManagerService;
import java.util.UUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestApplication {
@Before
public void before() throws Exception {
// Standardize override values before Spring Context bootstraps
SecretManagerService.setOverride("cloudinary_apiKey", UUID.randomUUID().toString());
SecretManagerService.setOverride("cloudinary_apiSecret", UUID.randomUUID().toString());
SecretManagerService.setOverride("twilio_accountSid", UUID.randomUUID().toString());
SecretManagerService.setOverride("twilio_accountToken", UUID.randomUUID().toString());
SecretManagerService.setOverride("twilio_serviceSid", UUID.randomUUID().toString());
}
@After
public void after() {
// Always clear overrides to keep tests clean and isolated
SecretManagerService.clearOverrides();
}
@Test
public void testSpringBootApplicationStartup() {
// Triggers full Spring application initialization
Application.main(new String[]{});
}
}