Skip to content

Commit af4e661

Browse files
CodingAnarchyclaude
andcommitted
feat: Add database-managed key rotation and enhanced configuration serialization v1.13.0
This release introduces comprehensive database-managed key rotation capabilities and fixes critical TOML configuration serialization issues. ## Key Features Added ### Database-Managed Key Rotation System - Complete PostgreSQL and MySQL support for automated key rotation - Rotation scheduling with configurable intervals and timestamps - Background rotation service for automated lifecycle management - Enhanced rotation detection using database-native time comparisons - Comprehensive rotation schedule management (update, query, schedule) - Real-time rotation status tracking and version management ### Enhanced Configuration Serialization - Fixed Duration serialization to use human-readable TOML format ("30s", "5m", "1h", "1d") - Fixed UUID serialization compatibility issues by converting to string format - Enhanced duration parsing supporting multiple formats and plain numbers - Removed duplicate struct definitions causing serialization conflicts ### Database Statistics Implementation - Real database statistics queries replacing placeholder implementations - Comprehensive key counts by status (Active, Retired, Revoked, Expired) - Database-specific date functions for accurate key age calculations - Expiration monitoring with 7-day early warning system - Integration tests for both PostgreSQL and MySQL backends ## Technical Improvements - Added Clone trait implementation for KeyManager background operations - Enhanced error handling and logging throughout rotation operations - Cross-database compatibility with functional equivalence - Production-ready database queries with proper indexing support 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent fe86bc5 commit af4e661

File tree

6 files changed

+2015
-126
lines changed

6 files changed

+2015
-126
lines changed

CHANGELOG.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [1.13.0] - 2025-07-16
99

1010
### Fixed
1111
- **⚙️ Configuration Serialization**
@@ -15,6 +15,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Enhanced duration parsing to support multiple formats: plain numbers (seconds), and suffixes (s, m, h, d)
1616
- Re-enabled previously ignored configuration file tests (`test_config_file_operations`)
1717

18+
### Enhanced
19+
- **🔐 Encryption Key Management Statistics**
20+
- Implemented real database statistics queries for encryption key management system
21+
- Added comprehensive PostgreSQL and MySQL statistics queries for key counts by status (Active, Retired, Revoked, Expired)
22+
- Enhanced key age calculation using database-specific date functions (PostgreSQL `EXTRACT(EPOCH)`, MySQL `TIMESTAMPDIFF`)
23+
- Added expiration monitoring with 7-day early warning for keys approaching expiration
24+
- Implemented rotation tracking for keys due for automated rotation
25+
- Added integration tests for statistics queries with both PostgreSQL and MySQL backends
26+
- Replaced placeholder statistics implementation with production-ready database queries
27+
28+
- **🔄 Database-Managed Key Rotation System**
29+
- Implemented complete database-managed key rotation with PostgreSQL and MySQL support
30+
- Added automatic key rotation scheduling with configurable intervals and next rotation timestamps
31+
- Enhanced rotation detection queries using database-native time comparisons
32+
- Implemented key rotation schedule management (update, query, schedule specific times)
33+
- Added background rotation service for automated key lifecycle management
34+
- Enhanced rotation methods with proper version management and status tracking
35+
- Added comprehensive integration tests for rotation functionality, scheduling, and automation
36+
- Implemented Clone trait for KeyManager to support background service operations
37+
1838
## [1.12.0] - 2025-07-15
1939

2040
### Added

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ members = [
99
resolver = "2"
1010

1111
[workspace.package]
12-
version = "1.12.0"
12+
version = "1.13.0"
1313
edition = "2024"
1414
license = "MIT"
1515
repository = "https://github.com/CodingAnarchy/hammerwork"

src/config.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,18 @@ use crate::{
1010
};
1111

1212
#[cfg(feature = "webhooks")]
13-
use crate::{
14-
streaming::StreamBackend,
15-
webhooks::WebhookConfig,
16-
};
13+
use crate::{streaming::StreamBackend, webhooks::WebhookConfig};
1714

1815
#[cfg(feature = "alerting")]
1916
use crate::alerting::AlertingConfig;
2017

2118
#[cfg(feature = "metrics")]
2219
use crate::metrics::MetricsConfig;
2320

21+
use crate::streaming::StreamConfig;
2422
use chrono::Duration;
2523
use serde::{Deserialize, Serialize};
2624
use std::{collections::HashMap, path::PathBuf, time::Duration as StdDuration};
27-
use crate::streaming::StreamConfig;
2825

2926
/// Module for serializing std::time::Duration as human-readable strings
3027
mod duration_secs {
@@ -52,35 +49,39 @@ mod duration_secs {
5249
D: Deserializer<'de>,
5350
{
5451
use serde::de::Error;
55-
52+
5653
let s = String::deserialize(deserializer)?;
5754
parse_duration(&s).map_err(D::Error::custom)
5855
}
5956

6057
/// Parse a duration string like "30s", "5m", "1h", "90", etc.
6158
fn parse_duration(s: &str) -> Result<Duration, String> {
6259
let s = s.trim();
63-
60+
6461
// Handle just numbers (assume seconds)
6562
if let Ok(secs) = s.parse::<u64>() {
6663
return Ok(Duration::from_secs(secs));
6764
}
68-
65+
6966
// Handle suffixed durations
7067
if s.len() < 2 {
7168
return Err(format!("Invalid duration format: {}", s));
7269
}
73-
70+
7471
let (num_str, suffix) = s.split_at(s.len() - 1);
75-
let num: u64 = num_str.parse()
72+
let num: u64 = num_str
73+
.parse()
7674
.map_err(|_| format!("Invalid number in duration: {}", num_str))?;
77-
75+
7876
match suffix {
7977
"s" => Ok(Duration::from_secs(num)),
8078
"m" => Ok(Duration::from_secs(num * 60)),
8179
"h" => Ok(Duration::from_secs(num * 3600)),
8280
"d" => Ok(Duration::from_secs(num * 86400)),
83-
_ => Err(format!("Invalid duration suffix: {}. Use s, m, h, or d", suffix)),
81+
_ => Err(format!(
82+
"Invalid duration suffix: {}. Use s, m, h, or d",
83+
suffix
84+
)),
8485
}
8586
}
8687
}
@@ -342,7 +343,6 @@ pub struct WebhookConfigs {
342343
pub global_settings: WebhookGlobalSettings,
343344
}
344345

345-
346346
/// Global webhook settings
347347
#[cfg(feature = "webhooks")]
348348
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -382,7 +382,6 @@ pub struct StreamingConfigs {
382382
pub global_settings: StreamingGlobalSettings,
383383
}
384384

385-
386385
/// Simple event filter for when webhooks feature is disabled
387386
#[cfg(not(feature = "webhooks"))]
388387
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -702,8 +701,14 @@ mod tests {
702701

703702
// Load back and verify values
704703
let loaded_config = HammerworkConfig::from_file(config_path.to_str().unwrap()).unwrap();
705-
assert_eq!(loaded_config.worker.polling_interval, StdDuration::from_secs(30));
706-
assert_eq!(loaded_config.worker.job_timeout, StdDuration::from_secs(300));
704+
assert_eq!(
705+
loaded_config.worker.polling_interval,
706+
StdDuration::from_secs(30)
707+
);
708+
assert_eq!(
709+
loaded_config.worker.job_timeout,
710+
StdDuration::from_secs(300)
711+
);
707712

708713
// Test parsing various duration formats
709714
let test_durations = [
@@ -768,7 +773,11 @@ json_format = false
768773
);
769774

770775
let config: HammerworkConfig = toml::from_str(&toml_content).unwrap();
771-
assert_eq!(config.worker.polling_interval, *expected, "Failed to parse duration: {}", duration_str);
776+
assert_eq!(
777+
config.worker.polling_interval, *expected,
778+
"Failed to parse duration: {}",
779+
duration_str
780+
);
772781
}
773782
}
774783

0 commit comments

Comments
 (0)