Update 6 files

- /src1/main/resources/application.yml
- /src1/main/java/com/v2d/document/config/AppProperties.java
- /src1/main/java/com/v2d/document/service/ExternalApiService.java
- /src1/main/java/com/v2d/document/controller/GenerateController.java
- /src1/test/AppPropertiesTest.java
- /README.md
This commit is contained in:
Aarthi Manivannan, Premanathan Aarthi Manivannan
2025-12-09 17:59:09 +01:00
parent 283b4ed6af
commit 55555bcc37
6 changed files with 154 additions and 0 deletions
+43
View File
@@ -91,3 +91,46 @@ For open source projects, say how it is licensed.
## Project status ## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
## 🔐 Secure API Key Management (Sprint 4 V2D Document)
This project uses secure environment variables to store and manage all external API keys
(required for LLM/Transcription APIs). No API key is ever committed into the repository.
### ✔ How the API Key Works
The application reads the key from an environment variable named:
`LLM_API_KEY`
Spring Boot loads it automatically using the following configuration in `application.yml`:
### ✔ Local Development (developer machines)
Developers must manually set their API key locally:
### ✔ GitLab CI/CD Setup (secure by default)
To provide the key for all environments securely:
1. Go to **GitLab → Settings → CI/CD → Variables**
2. Add variable:
- **Key:** `LLM_API_KEY`
- **Value:** your real API key
- **Masked:** ✓ Enable
- **Protected:** (optional)
3. Save.
Pipelines will automatically use the secure key without exposing it.
### ✔ Security Guarantees
- The API key is **not stored** in the repository
- `.env` files are ignored through `.gitignore`
- The key is **never printed**, logged, or exposed to users
- Every new user of V2D Document can use the system **without needing their own key**
### ✔ Files Added in This User Story
- `src/main/resources/application.yml`
- `src/main/java/com/v2d/document/config/AppProperties.java`
- `src/main/java/com/v2d/document/service/ExternalApiService.java`
- `src/test/java/com/v2d/document/config/AppPropertiesTest.java`
This completes Sprint 4 User Story: **Backend Secure Management & Storage of API Keys**.
@@ -0,0 +1,13 @@
package com.v2d.document.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "app.external")
public class AppProperties {
private String apiKey;
public String getApiKey() { return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
}
@@ -0,0 +1,26 @@
package com.v2d.document.controller;
import com.v2d.document.service.ExternalApiService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/generate")
public class GenerateController {
private final ExternalApiService externalApiService;
public GenerateController(ExternalApiService externalApiService) {
this.externalApiService = externalApiService;
}
@PostMapping
public ResponseEntity<String> generate(@RequestBody Map<String,Object> body) {
// Build provider payload from the user's body (transform safely)
String payload = "{\"text\": \"use this text\"}"; // adapt for real usage
String providerResponse = externalApiService.callProvider(payload);
return ResponseEntity.ok(providerResponse);
}
}
@@ -0,0 +1,45 @@
package com.v2d.document.service;
import com.v2d.document.config.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@Service
public class ExternalApiService {
private final Logger log = LoggerFactory.getLogger(ExternalApiService.class);
private final AppProperties props;
private final HttpClient http = HttpClient.newHttpClient();
public ExternalApiService(AppProperties props) {
this.props = props;
}
public String callProvider(String jsonPayload) {
String key = props.getApiKey();
if (key == null || key.isBlank()) {
log.warn("External API key is not configured.");
throw new IllegalStateException("External API key missing");
}
try {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/endpoint")) // replace with real endpoint
.header("Authorization", "Bearer " + key)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
return resp.body();
} catch (Exception e) {
log.error("External API call failed: {}", e.getMessage());
throw new RuntimeException("External API call failed", e);
}
}
}
+7
View File
@@ -0,0 +1,7 @@
spring:
application:
name: v2d-document
app:
external:
apiKey: ${LLM_API_KEY:}
+20
View File
@@ -0,0 +1,20 @@
package com.v2d.document.config;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
public class AppPropertiesTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(AppProperties.class)
.withPropertyValues("app.external.apiKey=TEST_KEY");
@Test
void bindsApiKey() {
contextRunner.run(context -> {
AppProperties props = context.getBean(AppProperties.class);
assertThat(props.getApiKey()).isEqualTo("TEST_KEY");
});
}
}