Skip to content

Commit 9baee58

Browse files
committed
log4j 2 implementation for logging
1 parent 4b4e69e commit 9baee58

4 files changed

Lines changed: 147 additions & 35 deletions

File tree

pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@
7575
<scope>test</scope>
7676
</dependency>
7777

78+
<dependency>
79+
<groupId>org.apache.logging.log4j</groupId>
80+
<artifactId>log4j-api</artifactId>
81+
<version>2.20.0</version> </dependency>
82+
<dependency>
83+
<groupId>org.apache.logging.log4j</groupId>
84+
<artifactId>log4j-core</artifactId>
85+
<version>2.20.0</version>
86+
</dependency>
87+
7888
</dependencies>
7989

8090
<dependencyManagement>

src/main/java/com/ayedata/simault/controller/AdminController.java

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.ayedata.simault.repository.AppRegistryRepository;
66
import com.mongodb.client.MongoClient;
77
import com.mongodb.client.MongoCollection;
8+
import org.apache.logging.log4j.LogManager;
9+
import org.apache.logging.log4j.Logger;
810
import org.bson.Document;
911
import org.bson.types.Binary;
1012
import org.springframework.beans.factory.annotation.Value;
@@ -21,6 +23,9 @@
2123
@RequestMapping("/api/admin")
2224
public class AdminController {
2325

26+
// 1. Initialize Log4j 2 Logger
27+
private static final Logger logger = LogManager.getLogger(AdminController.class);
28+
2429
private final AppRegistryRepository registry;
2530
private final MongoClient mongoClient;
2631

@@ -35,8 +40,18 @@ public AdminController(AppRegistryRepository registry, MongoClient mongoClient)
3540
this.mongoClient = mongoClient;
3641
}
3742

43+
/**
44+
* Helper to check authorization and log failures.
45+
*/
3846
private boolean isUnauthorized(String requestKey) {
39-
return requestKey == null || !requestKey.equals(adminApiKey);
47+
// Log the key with "token=" prefix so the Regex Replacement picks it up and redacts it.
48+
logger.debug("Validating admin access for token={}", requestKey);
49+
50+
if (requestKey == null || !requestKey.equals(adminApiKey)) {
51+
logger.warn("Unauthorized access attempt. Invalid or missing API key.");
52+
return true;
53+
}
54+
return false;
4055
}
4156

4257
// --- KEY ENDPOINT ---
@@ -52,38 +67,51 @@ public ResponseEntity<VaultKey> findKey(
5267

5368
// 1. Determine which key name to search for
5469
String searchName = (altName != null && !altName.isBlank()) ? altName : defaultKeyAltName;
55-
56-
// 2. Query MongoDB
57-
MongoCollection<Document> keyVault = mongoClient.getDatabase("encryption").getCollection("__keyVault");
58-
Document keyDoc = keyVault.find(new Document("keyAltNames", searchName)).first();
59-
60-
if (keyDoc == null) {
61-
return ResponseEntity.notFound().build();
70+
logger.info("Admin requesting key details for alias: '{}'", searchName);
71+
72+
try {
73+
// 2. Query MongoDB
74+
MongoCollection<Document> keyVault = mongoClient.getDatabase("encryption").getCollection("__keyVault");
75+
Document keyDoc = keyVault.find(new Document("keyAltNames", searchName)).first();
76+
77+
if (keyDoc == null) {
78+
logger.warn("Key not found for alias: '{}'", searchName);
79+
return ResponseEntity.notFound().build();
80+
}
81+
82+
// 3. Map BSON to Java Model (VaultKey)
83+
Binary bsonBinary = keyDoc.get("_id", Binary.class);
84+
ByteBuffer buffer = ByteBuffer.wrap(bsonBinary.getData());
85+
UUID keyId = new UUID(buffer.getLong(), buffer.getLong());
86+
87+
String provider = keyDoc.get("masterKey", Document.class).getString("provider");
88+
89+
VaultKey response = new VaultKey(
90+
searchName,
91+
keyId.toString(),
92+
"Active",
93+
provider
94+
);
95+
96+
logger.info("Key details retrieved successfully for alias: '{}'", searchName);
97+
return ResponseEntity.ok(response);
98+
99+
} catch (Exception e) {
100+
logger.error("Database error while retrieving key for alias: '{}'", searchName, e);
101+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
62102
}
63-
64-
// 3. Map BSON to Java Model (VaultKey)
65-
Binary bsonBinary = keyDoc.get("_id", Binary.class);
66-
ByteBuffer buffer = ByteBuffer.wrap(bsonBinary.getData());
67-
UUID keyId = new UUID(buffer.getLong(), buffer.getLong());
68-
69-
String provider = keyDoc.get("masterKey", Document.class).getString("provider");
70-
71-
VaultKey response = new VaultKey(
72-
searchName,
73-
keyId.toString(),
74-
"Active",
75-
provider
76-
);
77-
78-
return ResponseEntity.ok(response);
79103
}
80104

81105
// --- APP ENDPOINTS ---
82106

83107
@GetMapping("/apps")
84108
public ResponseEntity<List<AllowedApp>> listApps(@RequestHeader(value = "X-ADMIN-KEY", required = false) String apiKey) {
85109
if (isUnauthorized(apiKey)) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
110+
111+
logger.info("Fetching list of all allowed apps.");
86112
List<AllowedApp> apps = registry.findAll();
113+
logger.debug("Found {} registered apps.", apps.size());
114+
87115
return ResponseEntity.ok(apps);
88116
}
89117

@@ -95,10 +123,24 @@ public ResponseEntity<String> registerApp(
95123
if (isUnauthorized(apiKey)) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("⛔ Unauthorized");
96124

97125
String appId = payload.get("appId");
98-
if (appId == null || appId.isBlank()) return ResponseEntity.badRequest().body("appId is required");
126+
127+
// Log the payload safely. If payload contained "key":"..." or "token=...",
128+
// Log4j would automatically redact those specific fields.
129+
logger.info("Attempting to register app. Payload: {}", payload);
130+
131+
if (appId == null || appId.isBlank()) {
132+
logger.warn("Registration failed: Missing appId in payload.");
133+
return ResponseEntity.badRequest().body("appId is required");
134+
}
99135

100-
registry.registerApp(appId, payload.get("description"));
101-
return ResponseEntity.ok("✅ App registered: " + appId);
136+
try {
137+
registry.registerApp(appId, payload.get("description"));
138+
logger.info("✅ App registered successfully: {}", appId);
139+
return ResponseEntity.ok("✅ App registered: " + appId);
140+
} catch (Exception e) {
141+
logger.error("Failed to register app: {}", appId, e);
142+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Registration failed");
143+
}
102144
}
103145

104146
@DeleteMapping("/apps/{appId}")
@@ -107,7 +149,16 @@ public ResponseEntity<String> removeApp(
107149
@PathVariable String appId) {
108150

109151
if (isUnauthorized(apiKey)) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("⛔ Unauthorized");
110-
registry.removeApp(appId);
111-
return ResponseEntity.ok("🚫 Access revoked for: " + appId);
152+
153+
logger.info("Attempting to revoke access for app: {}", appId);
154+
155+
try {
156+
registry.removeApp(appId);
157+
logger.info("🚫 Access revoked successfully for: {}", appId);
158+
return ResponseEntity.ok("🚫 Access revoked for: " + appId);
159+
} catch (Exception e) {
160+
logger.error("Failed to revoke app: {}", appId, e);
161+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Revocation failed");
162+
}
112163
}
113164
}

src/main/java/com/ayedata/simault/controller/SecretController.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
import com.ayedata.simault.model.AppSecret;
44
import com.ayedata.simault.service.SecretVaultService;
5+
import org.apache.logging.log4j.LogManager;
6+
import org.apache.logging.log4j.Logger;
57
import org.springframework.http.ResponseEntity;
68
import org.springframework.web.bind.annotation.*;
79

810
@RestController
911
@RequestMapping("/api/secrets")
1012
public class SecretController {
1113

14+
// 1. Initialize Log4j 2 Logger
15+
private static final Logger logger = LogManager.getLogger(SecretController.class);
16+
1217
private final SecretVaultService vaultService;
1318

1419
// Notice: We DO NOT inject AppRegistryRepository here.
@@ -24,9 +29,22 @@ public SecretController(SecretVaultService vaultService) {
2429
*/
2530
@GetMapping("/{appId}")
2631
public ResponseEntity<AppSecret> getSecret(@PathVariable String appId) {
27-
// The service performs the "isAppAllowed()" check immediately.
28-
AppSecret secret = vaultService.getAppSecret(appId);
29-
return ResponseEntity.ok(secret);
32+
logger.info("Request received to retrieve secret for appId: {}", appId);
33+
34+
try {
35+
// The service performs the "isAppAllowed()" check immediately.
36+
AppSecret secret = vaultService.getAppSecret(appId);
37+
38+
// We log success, but we avoid logging the 'secret' object itself to prevent
39+
// accidental leakage, even though our Log4j regex would likely catch it.
40+
logger.info("Secret successfully retrieved for appId: {}", appId);
41+
42+
return ResponseEntity.ok(secret);
43+
} catch (Exception e) {
44+
// Log the error. If the error message contains sensitive info, Log4j will redact it.
45+
logger.error("Failed to retrieve secret for appId: {}", appId, e);
46+
throw e; // Re-throw to let Spring handle the error response (e.g., 403 or 500)
47+
}
3048
}
3149

3250
/**
@@ -36,8 +54,18 @@ public ResponseEntity<AppSecret> getSecret(@PathVariable String appId) {
3654
*/
3755
@PostMapping("/{appId}/rotate")
3856
public ResponseEntity<AppSecret> rotateSecret(@PathVariable String appId) {
39-
// The service performs the "isAppAllowed()" check immediately.
40-
AppSecret secret = vaultService.rotateSecret(appId);
41-
return ResponseEntity.ok(secret);
57+
logger.warn("Manual secret rotation requested for appId: {}", appId);
58+
59+
try {
60+
// The service performs the "isAppAllowed()" check immediately.
61+
AppSecret secret = vaultService.rotateSecret(appId);
62+
63+
logger.info("Secret successfully rotated for appId: {}", appId);
64+
65+
return ResponseEntity.ok(secret);
66+
} catch (Exception e) {
67+
logger.error("Failed to rotate secret for appId: {}", appId, e);
68+
throw e;
69+
}
4270
}
4371
}

src/main/resources/log4j2.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="WARN">
3+
<Appenders>
4+
<Console name="Console" target="SYSTEM_OUT">
5+
<PatternLayout>
6+
<Pattern>%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %replace{%msg}{(?i)(Bearer\s+|token=|password=|key":")([a-zA-Z0-9\._-]+)}{$1*****REDACTED*****}%n</Pattern>
7+
</PatternLayout>
8+
</Console>
9+
10+
<File name="File" fileName="logs/app.log">
11+
<PatternLayout>
12+
<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %replace{%msg}{(?i)(Bearer\s+|token=|password=|key":")([a-zA-Z0-9\._-]+)}{$1*****REDACTED*****}%n</Pattern>
13+
</PatternLayout>
14+
</File>
15+
</Appenders>
16+
17+
<Loggers>
18+
<Root level="info">
19+
<AppenderRef ref="Console"/>
20+
<AppenderRef ref="File"/>
21+
</Root>
22+
</Loggers>
23+
</Configuration>

0 commit comments

Comments
 (0)