-
This commit is contained in:
879
docs/SYSTEM_IMPLEMENTATION_GUIDE.md
Normal file
879
docs/SYSTEM_IMPLEMENTATION_GUIDE.md
Normal file
@ -0,0 +1,879 @@
|
||||
# KMS System Implementation Guide
|
||||
|
||||
This document provides detailed implementation guidance for the KMS system, covering areas not extensively documented in other files. It serves as a comprehensive reference for developers working on system components.
|
||||
|
||||
## Table of Contents
|
||||
1. [Documentation Consistency Analysis](#documentation-consistency-analysis)
|
||||
2. [Audit System Implementation](#audit-system-implementation)
|
||||
3. [Multi-Tenancy Support](#multi-tenancy-support)
|
||||
4. [Cache Implementation Details](#cache-implementation-details)
|
||||
5. [Error Handling Framework](#error-handling-framework)
|
||||
6. [Validation System](#validation-system)
|
||||
7. [Metrics and Monitoring](#metrics-and-monitoring)
|
||||
8. [Database Migration System](#database-migration-system)
|
||||
9. [Frontend Architecture](#frontend-architecture)
|
||||
10. [Configuration Management](#configuration-management)
|
||||
|
||||
---
|
||||
|
||||
## Documentation Consistency Analysis
|
||||
|
||||
### Current State Assessment
|
||||
|
||||
The existing documentation is comprehensive but has some minor inconsistencies with the actual codebase:
|
||||
|
||||
#### ✅ Accurate Documentation Areas:
|
||||
- **API endpoints** match the implementation in handlers
|
||||
- **Database schema** aligns with migrations (especially the new audit_events table)
|
||||
- **Authentication flows** are correctly documented
|
||||
- **Docker compose setup** matches actual configuration
|
||||
- **Security architecture** accurately reflects implementation
|
||||
- **Permission system** documentation is consistent with code
|
||||
|
||||
#### ⚠️ Minor Inconsistencies Found:
|
||||
1. **Port references**: Some docs mention port 80 but actual nginx runs on 8081
|
||||
2. **Container names**: Documentation uses generic names, actual compose uses specific names like `kms-postgres`
|
||||
3. **Rate limiting values**: Docs show different values than actual middleware implementation
|
||||
4. **Frontend build process**: React version mentioned as 18, but package.json shows 19+
|
||||
|
||||
#### ✨ Recently Added Features (Not in Original Docs):
|
||||
- **Audit system** with comprehensive event logging
|
||||
- **Multi-tenancy support** in database schema
|
||||
- **Advanced caching layer** with Redis integration
|
||||
- **SAML authentication** implementation
|
||||
- **Advanced security middleware** with brute force protection
|
||||
|
||||
---
|
||||
|
||||
## Audit System Implementation
|
||||
|
||||
### Overview
|
||||
|
||||
The KMS implements a comprehensive audit logging system that tracks all system events, user actions, and security-related activities.
|
||||
|
||||
### Core Components
|
||||
|
||||
#### Audit Event Structure
|
||||
```go
|
||||
// File: internal/audit/audit.go
|
||||
type AuditEvent struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Type EventType `json:"type"`
|
||||
Severity Severity `json:"severity"`
|
||||
Status Status `json:"status"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
|
||||
// Actor information
|
||||
ActorID string `json:"actor_id"`
|
||||
ActorType ActorType `json:"actor_type"`
|
||||
ActorIP string `json:"actor_ip"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
|
||||
// Multi-tenancy support
|
||||
TenantID *uuid.UUID `json:"tenant_id,omitempty"`
|
||||
|
||||
// Resource information
|
||||
ResourceID string `json:"resource_id"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
|
||||
// Event details
|
||||
Action string `json:"action"`
|
||||
Description string `json:"description"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
|
||||
// Request context
|
||||
RequestID string `json:"request_id"`
|
||||
SessionID string `json:"session_id"`
|
||||
|
||||
// Metadata
|
||||
Tags []string `json:"tags"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Event Types Taxonomy
|
||||
```
|
||||
auth.* - Authentication events
|
||||
├── auth.login - Successful user login
|
||||
├── auth.login_failed - Failed login attempt
|
||||
├── auth.logout - User logout
|
||||
├── auth.token_created - Token generation
|
||||
├── auth.token_revoked - Token revocation
|
||||
└── auth.token_validated - Token validation
|
||||
|
||||
session.* - Session management
|
||||
├── session.created - New session created
|
||||
├── session.revoked - Session terminated
|
||||
└── session.expired - Session timeout
|
||||
|
||||
app.* - Application management
|
||||
├── app.created - Application created
|
||||
├── app.updated - Application modified
|
||||
└── app.deleted - Application removed
|
||||
|
||||
permission.* - Permission operations
|
||||
├── permission.granted - Permission assigned
|
||||
├── permission.revoked - Permission removed
|
||||
└── permission.denied - Access denied
|
||||
|
||||
tenant.* - Multi-tenant operations
|
||||
├── tenant.created - New tenant
|
||||
├── tenant.updated - Tenant modified
|
||||
├── tenant.suspended - Tenant suspended
|
||||
└── tenant.activated - Tenant reactivated
|
||||
```
|
||||
|
||||
#### Database Schema
|
||||
```sql
|
||||
-- File: migrations/004_add_audit_events.up.sql
|
||||
CREATE TABLE audit_events (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
type VARCHAR(50) NOT NULL,
|
||||
severity VARCHAR(20) NOT NULL CHECK (severity IN ('info', 'warning', 'error', 'critical')),
|
||||
status VARCHAR(20) NOT NULL CHECK (status IN ('success', 'failure', 'pending')),
|
||||
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Actor information
|
||||
actor_id VARCHAR(255),
|
||||
actor_type VARCHAR(50) CHECK (actor_type IN ('user', 'system', 'service')),
|
||||
actor_ip INET,
|
||||
user_agent TEXT,
|
||||
|
||||
-- Multi-tenancy
|
||||
tenant_id UUID,
|
||||
|
||||
-- Resource tracking
|
||||
resource_id VARCHAR(255),
|
||||
resource_type VARCHAR(100),
|
||||
action VARCHAR(100) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
details JSONB DEFAULT '{}',
|
||||
|
||||
-- Request context
|
||||
request_id VARCHAR(100),
|
||||
session_id VARCHAR(255),
|
||||
|
||||
-- Metadata
|
||||
tags TEXT[],
|
||||
metadata JSONB DEFAULT '{}'
|
||||
);
|
||||
```
|
||||
|
||||
#### Frontend Integration
|
||||
```typescript
|
||||
// File: kms-frontend/src/components/Audit.tsx
|
||||
interface AuditEvent {
|
||||
id: string;
|
||||
type: string;
|
||||
severity: 'info' | 'warning' | 'error' | 'critical';
|
||||
status: 'success' | 'failure' | 'pending';
|
||||
timestamp: string;
|
||||
actor_id: string;
|
||||
actor_type: string;
|
||||
resource_type: string;
|
||||
action: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const Audit: React.FC = () => {
|
||||
// Real-time audit log viewing with filtering
|
||||
// Timeline view for event sequences
|
||||
// Statistics dashboard for audit metrics
|
||||
};
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
#### Logging Best Practices
|
||||
1. **Log all security-relevant events**
|
||||
2. **Include sufficient context** for forensic analysis
|
||||
3. **Use structured logging** with consistent fields
|
||||
4. **Implement log retention policies**
|
||||
5. **Ensure tamper-evident logging**
|
||||
|
||||
#### Performance Considerations
|
||||
1. **Asynchronous logging** to avoid blocking operations
|
||||
2. **Batch inserts** for high-volume events
|
||||
3. **Proper indexing** on commonly queried fields
|
||||
4. **Archival strategy** for historical data
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenancy Support
|
||||
|
||||
### Architecture
|
||||
|
||||
The KMS implements a multi-tenant architecture where each tenant has isolated data and permissions while sharing the same application instance.
|
||||
|
||||
### Database Design
|
||||
|
||||
#### Tenant Model
|
||||
```go
|
||||
// File: internal/domain/tenant.go
|
||||
type Tenant struct {
|
||||
ID uuid.UUID `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
Slug string `json:"slug" db:"slug"`
|
||||
Status TenantStatus `json:"status" db:"status"`
|
||||
Settings TenantSettings `json:"settings" db:"settings"`
|
||||
Metadata map[string]interface{} `json:"metadata" db:"metadata"`
|
||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
type TenantStatus string
|
||||
|
||||
const (
|
||||
TenantStatusActive TenantStatus = "active"
|
||||
TenantStatusSuspended TenantStatus = "suspended"
|
||||
TenantStatusPending TenantStatus = "pending"
|
||||
)
|
||||
```
|
||||
|
||||
#### Data Isolation Strategy
|
||||
```sql
|
||||
-- All tenant-specific tables include tenant_id
|
||||
ALTER TABLE applications ADD COLUMN tenant_id UUID REFERENCES tenants(id);
|
||||
ALTER TABLE static_tokens ADD COLUMN tenant_id UUID REFERENCES tenants(id);
|
||||
ALTER TABLE user_sessions ADD COLUMN tenant_id UUID REFERENCES tenants(id);
|
||||
ALTER TABLE audit_events ADD COLUMN tenant_id UUID;
|
||||
|
||||
-- Row-level security policies
|
||||
ALTER TABLE applications ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation ON applications
|
||||
FOR ALL TO kms_user
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
#### Tenant Context Middleware
|
||||
```go
|
||||
// File: internal/middleware/tenant.go
|
||||
func TenantMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
tenantID := extractTenantID(c)
|
||||
if tenantID == "" {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": "tenant_required"})
|
||||
return
|
||||
}
|
||||
|
||||
// Set tenant context
|
||||
c.Set("tenant_id", tenantID)
|
||||
|
||||
// Set database session variable
|
||||
db := c.MustGet("db").(*sql.DB)
|
||||
_, err := db.Exec("SELECT set_config('app.current_tenant', $1, true)", tenantID)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(500, gin.H{"error": "tenant_setup_failed"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Guidelines
|
||||
|
||||
1. **Always include tenant_id** in database queries
|
||||
2. **Validate tenant access** in middleware
|
||||
3. **Implement tenant-aware caching**
|
||||
4. **Audit cross-tenant operations**
|
||||
5. **Test tenant isolation thoroughly**
|
||||
|
||||
---
|
||||
|
||||
## Cache Implementation Details
|
||||
|
||||
### Architecture
|
||||
|
||||
The KMS implements a layered caching system with multiple providers and configurable TTL policies.
|
||||
|
||||
### Cache Interface
|
||||
```go
|
||||
// File: internal/cache/cache.go
|
||||
type CacheManager interface {
|
||||
Get(ctx context.Context, key string) ([]byte, error)
|
||||
Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
|
||||
GetJSON(ctx context.Context, key string, dest interface{}) error
|
||||
SetJSON(ctx context.Context, key string, value interface{}, ttl time.Duration) error
|
||||
Delete(ctx context.Context, key string) error
|
||||
Clear(ctx context.Context) error
|
||||
Exists(ctx context.Context, key string) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
### Redis Implementation
|
||||
```go
|
||||
// File: internal/cache/redis.go
|
||||
type RedisCacheManager struct {
|
||||
client redis.Client
|
||||
keyPrefix string
|
||||
serializer JSONSerializer
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (r *RedisCacheManager) GetJSON(ctx context.Context, key string, dest interface{}) error {
|
||||
prefixedKey := r.keyPrefix + key
|
||||
data, err := r.client.Get(ctx, prefixedKey).Bytes()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return ErrCacheMiss
|
||||
}
|
||||
return fmt.Errorf("failed to get key %s: %w", prefixedKey, err)
|
||||
}
|
||||
|
||||
return r.serializer.Deserialize(data, dest)
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Key Management
|
||||
```go
|
||||
type CacheKey string
|
||||
|
||||
const (
|
||||
KeyPrefixAuth = "auth:"
|
||||
KeyPrefixToken = "token:"
|
||||
KeyPrefixPermission = "perm:"
|
||||
KeyPrefixSession = "sess:"
|
||||
KeyPrefixApp = "app:"
|
||||
)
|
||||
|
||||
func CacheKey(prefix, suffix string) string {
|
||||
return fmt.Sprintf("%s%s", prefix, suffix)
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Patterns
|
||||
|
||||
#### Authentication Caching
|
||||
```go
|
||||
// Cache authentication results for 5 minutes
|
||||
cacheKey := cache.CacheKey(cache.KeyPrefixAuth, fmt.Sprintf("%s:%s", userID, appID))
|
||||
err := cacheManager.SetJSON(ctx, cacheKey, authResult, 5*time.Minute)
|
||||
```
|
||||
|
||||
#### Token Revocation List
|
||||
```go
|
||||
// Cache revoked tokens until their expiration
|
||||
revokedKey := cache.CacheKey(cache.KeyPrefixToken, "revoked:"+tokenID)
|
||||
err := cacheManager.Set(ctx, revokedKey, []byte("1"), tokenExpiry.Sub(time.Now()))
|
||||
```
|
||||
|
||||
### Configuration
|
||||
```bash
|
||||
# Cache configuration
|
||||
CACHE_ENABLED=true
|
||||
CACHE_PROVIDER=redis # or memory
|
||||
REDIS_ADDR=localhost:6379
|
||||
REDIS_PASSWORD=
|
||||
REDIS_DB=0
|
||||
CACHE_DEFAULT_TTL=5m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling Framework
|
||||
|
||||
### Error Type Hierarchy
|
||||
```go
|
||||
// File: internal/errors/errors.go
|
||||
type ErrorCode string
|
||||
|
||||
const (
|
||||
ErrorCodeValidation ErrorCode = "validation_error"
|
||||
ErrorCodeAuthentication ErrorCode = "authentication_error"
|
||||
ErrorCodeAuthorization ErrorCode = "authorization_error"
|
||||
ErrorCodeNotFound ErrorCode = "not_found"
|
||||
ErrorCodeConflict ErrorCode = "conflict"
|
||||
ErrorCodeInternal ErrorCode = "internal_error"
|
||||
ErrorCodeRateLimit ErrorCode = "rate_limit_exceeded"
|
||||
ErrorCodeBadRequest ErrorCode = "bad_request"
|
||||
)
|
||||
|
||||
type APIError struct {
|
||||
Code ErrorCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details interface{} `json:"details,omitempty"`
|
||||
HTTPStatus int `json:"-"`
|
||||
Cause error `json:"-"`
|
||||
}
|
||||
```
|
||||
|
||||
### Error Factory Functions
|
||||
```go
|
||||
func NewValidationError(message string, details interface{}) *APIError {
|
||||
return &APIError{
|
||||
Code: ErrorCodeValidation,
|
||||
Message: message,
|
||||
Details: details,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuthenticationError(message string) *APIError {
|
||||
return &APIError{
|
||||
Code: ErrorCodeAuthentication,
|
||||
Message: message,
|
||||
HTTPStatus: http.StatusUnauthorized,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handler Middleware
|
||||
```go
|
||||
// File: internal/errors/secure_responses.go
|
||||
func (e *ErrorHandler) HandleError(c *gin.Context, err error) {
|
||||
var apiErr *APIError
|
||||
if errors.As(err, &apiErr) {
|
||||
// Log error with context
|
||||
e.logger.Error("API error",
|
||||
zap.String("error_code", string(apiErr.Code)),
|
||||
zap.String("message", apiErr.Message),
|
||||
zap.Int("http_status", apiErr.HTTPStatus),
|
||||
zap.Error(apiErr.Cause))
|
||||
|
||||
c.JSON(apiErr.HTTPStatus, gin.H{
|
||||
"error": apiErr.Code,
|
||||
"message": apiErr.Message,
|
||||
"details": apiErr.Details,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Handle unexpected errors
|
||||
e.logger.Error("Unexpected error", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": ErrorCodeInternal,
|
||||
"message": "An internal error occurred",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation System
|
||||
|
||||
### Validator Implementation
|
||||
```go
|
||||
// File: internal/validation/validator.go
|
||||
type Validator struct {
|
||||
validator *validator.Validate
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewValidator(logger *zap.Logger) *Validator {
|
||||
v := validator.New()
|
||||
|
||||
// Register custom validators
|
||||
v.RegisterValidation("app_id", validateAppID)
|
||||
v.RegisterValidation("token_type", validateTokenType)
|
||||
v.RegisterValidation("permission_scope", validatePermissionScope)
|
||||
|
||||
return &Validator{
|
||||
validator: v,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Validation Rules
|
||||
```go
|
||||
func validateAppID(fl validator.FieldLevel) bool {
|
||||
appID := fl.Field().String()
|
||||
// App ID format: domain.app (e.g., com.example.app)
|
||||
pattern := `^[a-z0-9]+(\.[a-z0-9]+)*\.[a-z0-9]+$`
|
||||
match, _ := regexp.MatchString(pattern, appID)
|
||||
return match && len(appID) >= 3 && len(appID) <= 100
|
||||
}
|
||||
|
||||
func validatePermissionScope(fl validator.FieldLevel) bool {
|
||||
scope := fl.Field().String()
|
||||
// Permission format: domain.action (e.g., app.read)
|
||||
pattern := `^[a-z_]+(\.[a-z_]+)*$`
|
||||
match, _ := regexp.MatchString(pattern, scope)
|
||||
return match && len(scope) >= 1 && len(scope) <= 50
|
||||
}
|
||||
```
|
||||
|
||||
### Middleware Integration
|
||||
```go
|
||||
// File: internal/middleware/validation.go
|
||||
func (v *ValidationMiddleware) ValidateJSON(schema interface{}) gin.HandlerFunc {
|
||||
return gin.HandlerFunc(func(c *gin.Context) {
|
||||
if err := c.ShouldBindJSON(schema); err != nil {
|
||||
var validationErrors []ValidationError
|
||||
|
||||
if errs, ok := err.(validator.ValidationErrors); ok {
|
||||
for _, e := range errs {
|
||||
validationErrors = append(validationErrors, ValidationError{
|
||||
Field: e.Field(),
|
||||
Message: e.Tag(),
|
||||
Value: e.Value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
apiErr := errors.NewValidationError("Request validation failed", validationErrors)
|
||||
v.errorHandler.HandleError(c, apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Metrics and Monitoring
|
||||
|
||||
### Prometheus Integration
|
||||
```go
|
||||
// File: internal/metrics/metrics.go
|
||||
type Metrics struct {
|
||||
// HTTP metrics
|
||||
httpRequestsTotal *prometheus.CounterVec
|
||||
httpRequestDuration *prometheus.HistogramVec
|
||||
httpRequestsInFlight prometheus.Gauge
|
||||
|
||||
// Auth metrics
|
||||
authAttemptsTotal *prometheus.CounterVec
|
||||
authSuccessTotal *prometheus.CounterVec
|
||||
authFailuresTotal *prometheus.CounterVec
|
||||
|
||||
// Token metrics
|
||||
tokensIssuedTotal *prometheus.CounterVec
|
||||
tokenValidationsTotal *prometheus.CounterVec
|
||||
|
||||
// Business metrics
|
||||
applicationsTotal prometheus.Gauge
|
||||
activeSessionsTotal prometheus.Gauge
|
||||
}
|
||||
```
|
||||
|
||||
### Metrics Collection
|
||||
```go
|
||||
func (m *Metrics) RecordHTTPRequest(method, path string, statusCode int, duration time.Duration) {
|
||||
m.httpRequestsTotal.WithLabelValues(method, path, strconv.Itoa(statusCode)).Inc()
|
||||
m.httpRequestDuration.WithLabelValues(method, path).Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
func (m *Metrics) RecordAuthAttempt(provider, result string) {
|
||||
m.authAttemptsTotal.WithLabelValues(provider, result).Inc()
|
||||
if result == "success" {
|
||||
m.authSuccessTotal.WithLabelValues(provider).Inc()
|
||||
} else {
|
||||
m.authFailuresTotal.WithLabelValues(provider).Inc()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dashboard Configuration
|
||||
```yaml
|
||||
# Grafana dashboard config
|
||||
panels:
|
||||
- title: "Request Rate"
|
||||
type: "graph"
|
||||
targets:
|
||||
- expr: "rate(http_requests_total[5m])"
|
||||
legendFormat: "{{method}} {{path}}"
|
||||
|
||||
- title: "Authentication Success Rate"
|
||||
type: "stat"
|
||||
targets:
|
||||
- expr: "rate(auth_success_total[5m]) / rate(auth_attempts_total[5m]) * 100"
|
||||
legendFormat: "Success Rate %"
|
||||
|
||||
- title: "Active Applications"
|
||||
type: "stat"
|
||||
targets:
|
||||
- expr: "applications_total"
|
||||
legendFormat: "Applications"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Migration System
|
||||
|
||||
### Migration Structure
|
||||
```
|
||||
migrations/
|
||||
├── 001_initial_schema.up.sql
|
||||
├── 001_initial_schema.down.sql
|
||||
├── 002_user_sessions.up.sql
|
||||
├── 002_user_sessions.down.sql
|
||||
├── 003_add_token_prefix.up.sql
|
||||
├── 003_add_token_prefix.down.sql
|
||||
├── 004_add_audit_events.up.sql
|
||||
└── 004_add_audit_events.down.sql
|
||||
```
|
||||
|
||||
### Migration Runner
|
||||
```go
|
||||
// File: internal/database/postgres.go
|
||||
func RunMigrations(db *sql.DB, migrationPath string) error {
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create migration driver: %w", err)
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithDatabaseInstance(
|
||||
fmt.Sprintf("file://%s", migrationPath),
|
||||
"postgres", driver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create migration instance: %w", err)
|
||||
}
|
||||
|
||||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||
return fmt.Errorf("failed to run migrations: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Migration Best Practices
|
||||
|
||||
1. **Always create both up and down migrations**
|
||||
2. **Test migrations on copy of production data**
|
||||
3. **Make migrations idempotent**
|
||||
4. **Add proper indexes for performance**
|
||||
5. **Include rollback procedures**
|
||||
|
||||
### Example Migration
|
||||
```sql
|
||||
-- 005_add_oauth_providers.up.sql
|
||||
CREATE TABLE IF NOT EXISTS oauth_providers (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
client_id VARCHAR(255) NOT NULL,
|
||||
client_secret_encrypted TEXT NOT NULL,
|
||||
authorization_url TEXT NOT NULL,
|
||||
token_url TEXT NOT NULL,
|
||||
user_info_url TEXT NOT NULL,
|
||||
scopes TEXT[] DEFAULT ARRAY['openid', 'profile', 'email'],
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_oauth_providers_name ON oauth_providers(name);
|
||||
CREATE INDEX idx_oauth_providers_enabled ON oauth_providers(enabled) WHERE enabled = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### Component Structure
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ ├── Applications.tsx # Application management
|
||||
│ ├── Tokens.tsx # Token operations
|
||||
│ ├── Users.tsx # User management
|
||||
│ ├── Audit.tsx # Audit log viewer
|
||||
│ ├── Dashboard.tsx # Main dashboard
|
||||
│ ├── Login.tsx # Authentication
|
||||
│ ├── TokenTester.tsx # Token testing utility
|
||||
│ └── TokenTesterCallback.tsx
|
||||
├── contexts/
|
||||
│ └── AuthContext.tsx # Authentication state
|
||||
├── services/
|
||||
│ └── apiService.ts # API client
|
||||
├── App.tsx # Main application
|
||||
└── index.tsx # Entry point
|
||||
```
|
||||
|
||||
### API Service Implementation
|
||||
```typescript
|
||||
// File: kms-frontend/src/services/apiService.ts
|
||||
class APIService {
|
||||
private baseURL: string;
|
||||
private token: string | null = null;
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.baseURL = baseURL;
|
||||
}
|
||||
|
||||
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
||||
const url = `${this.baseURL}${endpoint}`;
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-User-Email': this.getUserEmail(),
|
||||
...options.headers,
|
||||
};
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({}));
|
||||
throw new APIError(error.message || 'Request failed', response.status);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Application management
|
||||
async getApplications(): Promise<Application[]> {
|
||||
return this.request<Application[]>('/api/applications');
|
||||
}
|
||||
|
||||
// Audit log access
|
||||
async getAuditEvents(params: AuditQueryParams): Promise<AuditEvent[]> {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
return this.request<AuditEvent[]>(`/api/audit/events?${queryString}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Context
|
||||
```typescript
|
||||
// File: kms-frontend/src/contexts/AuthContext.tsx
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
login: (email: string) => Promise<void>;
|
||||
logout: () => void;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export const AuthContext = React.createContext<AuthContextType | null>(null);
|
||||
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const login = async (email: string) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiService.login(email);
|
||||
setUser({ email, token: response.token });
|
||||
localStorage.setItem('kms_user', JSON.stringify({ email }));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ... rest of implementation
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Configuration Interface
|
||||
```go
|
||||
// File: internal/config/config.go
|
||||
type ConfigProvider interface {
|
||||
GetString(key string) string
|
||||
GetInt(key string) int
|
||||
GetBool(key string) bool
|
||||
GetDuration(key string) time.Duration
|
||||
GetStringSlice(key string) []string
|
||||
IsSet(key string) bool
|
||||
Validate() error
|
||||
GetDatabaseDSN() string
|
||||
GetServerAddress() string
|
||||
IsDevelopment() bool
|
||||
IsProduction() bool
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Validation
|
||||
```go
|
||||
func (c *Config) Validate() error {
|
||||
var errors []string
|
||||
|
||||
// Required configuration
|
||||
required := []string{
|
||||
"INTERNAL_HMAC_KEY",
|
||||
"JWT_SECRET",
|
||||
"AUTH_SIGNING_KEY",
|
||||
"DB_HOST",
|
||||
"DB_NAME",
|
||||
}
|
||||
|
||||
for _, key := range required {
|
||||
if !c.IsSet(key) {
|
||||
errors = append(errors, fmt.Sprintf("required configuration %s is not set", key))
|
||||
}
|
||||
}
|
||||
|
||||
// Validate key lengths
|
||||
if len(c.GetString("INTERNAL_HMAC_KEY")) < 32 {
|
||||
errors = append(errors, "INTERNAL_HMAC_KEY must be at least 32 characters")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("configuration validation failed: %s", strings.Join(errors, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Configuration
|
||||
```bash
|
||||
# Security Configuration
|
||||
INTERNAL_HMAC_KEY=3924f352b7ea63b27db02bf4b0014f2961a5d2f7c27643853a4581bb3a5457cb
|
||||
JWT_SECRET=7f5e11d55e957988b00ce002418680af384219ef98c50d08cbbbdd541978450c
|
||||
AUTH_SIGNING_KEY=484f921b39c383e6b3e0cc5a7cef3c2cec3d7c8d474ab5102891dc4c2bf63a68
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=kms
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=postgres
|
||||
|
||||
# Feature Flags
|
||||
RATE_LIMIT_ENABLED=true
|
||||
CACHE_ENABLED=false
|
||||
METRICS_ENABLED=true
|
||||
SAML_ENABLED=false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Best Practices
|
||||
|
||||
### Code Organization
|
||||
1. **Follow clean architecture principles**
|
||||
2. **Use dependency injection throughout**
|
||||
3. **Implement comprehensive error handling**
|
||||
4. **Add structured logging to all components**
|
||||
5. **Write unit tests for business logic**
|
||||
|
||||
### Security Guidelines
|
||||
1. **Always validate input at API boundaries**
|
||||
2. **Use parameterized database queries**
|
||||
3. **Implement proper authentication and authorization**
|
||||
4. **Log all security-relevant events**
|
||||
5. **Follow principle of least privilege**
|
||||
|
||||
### Performance Considerations
|
||||
1. **Implement caching for frequently accessed data**
|
||||
2. **Use database indexes appropriately**
|
||||
3. **Monitor and optimize slow queries**
|
||||
4. **Implement proper connection pooling**
|
||||
5. **Use asynchronous operations where beneficial**
|
||||
|
||||
### Testing Strategy
|
||||
1. **Unit tests for business logic**
|
||||
2. **Integration tests for API endpoints**
|
||||
3. **End-to-end tests for critical workflows**
|
||||
4. **Load testing for performance validation**
|
||||
5. **Security testing for vulnerability assessment**
|
||||
|
||||
---
|
||||
|
||||
*This document serves as a comprehensive implementation guide for the KMS system. It should be updated as the system evolves and new features are added.*
|
||||
Reference in New Issue
Block a user