remove docs
This commit is contained in:
@@ -1,506 +0,0 @@
|
|||||||
# Horizontal Scalability Implementation
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Your authorization microservice is now **fully horizontally scalable** using Redis-based distributed caching. Multiple instances can run concurrently with shared state across all nodes.
|
|
||||||
|
|
||||||
## Implementation Summary
|
|
||||||
|
|
||||||
### What Was Changed
|
|
||||||
|
|
||||||
#### 1. Distributed Caching (`services/cached_authorization.go`)
|
|
||||||
|
|
||||||
- **Permission Cache**: Moved from local `sync.RWMutex` maps to Redis with key pattern `authz:perm:resource:action`
|
|
||||||
- **Policy Cache**: Stored in Redis with key pattern `authz:policy:permissionID`
|
|
||||||
- **User Attributes Cache**: Stored in Redis with key pattern `authz:userattr:userID`
|
|
||||||
- **Cache TTL**: 30 seconds for automatic expiration
|
|
||||||
- **Fallback Strategy**: Local cache maintained for backward compatibility and resilience
|
|
||||||
|
|
||||||
#### 2. Cache Architecture
|
|
||||||
|
|
||||||
```text
|
|
||||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
||||||
│ Instance 1 │ │ Instance 2 │ │ Instance 3 │
|
|
||||||
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
|
||||||
│ │ │
|
|
||||||
└───────────────────┼───────────────────┘
|
|
||||||
│
|
|
||||||
┌──────▼──────┐
|
|
||||||
│ Redis │
|
|
||||||
│ (Distributed)│
|
|
||||||
│ Cache │
|
|
||||||
└─────────────┘
|
|
||||||
│
|
|
||||||
┌──────▼──────┐
|
|
||||||
│ PostgreSQL │
|
|
||||||
│ (Database) │
|
|
||||||
└─────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Key Features
|
|
||||||
|
|
||||||
#### Dual-Layer Caching
|
|
||||||
|
|
||||||
- Primary: Redis (distributed, shared across instances)
|
|
||||||
- Secondary: Local in-memory (failover, performance boost)
|
|
||||||
- Automatic fallback when Redis unavailable
|
|
||||||
|
|
||||||
#### Consistency Guarantees
|
|
||||||
|
|
||||||
- All instances share the same Redis cache
|
|
||||||
- 30-second automatic cache refresh
|
|
||||||
- Manual invalidation via `InvalidateUserCache()`
|
|
||||||
- Force refresh via `RefreshCacheNow()`
|
|
||||||
|
|
||||||
#### Performance Optimizations
|
|
||||||
|
|
||||||
- JSON serialization for complex objects
|
|
||||||
- 100ms timeout for Redis operations
|
|
||||||
- Non-blocking Redis writes
|
|
||||||
- Concurrent-safe operations
|
|
||||||
|
|
||||||
## Deployment Patterns
|
|
||||||
|
|
||||||
### Kubernetes Deployment
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: authorization-service
|
|
||||||
spec:
|
|
||||||
replicas: 5 # Scale as needed
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: authorization
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: authorization
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: authorization
|
|
||||||
image: your-registry/authorization:latest
|
|
||||||
env:
|
|
||||||
- name: REDIS_HOST
|
|
||||||
value: "redis-cluster.default.svc.cluster.local"
|
|
||||||
- name: REDIS_PORT
|
|
||||||
value: "6379"
|
|
||||||
- name: REDIS_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: redis-secret
|
|
||||||
key: password
|
|
||||||
- name: DB_HOST
|
|
||||||
value: "postgres.default.svc.cluster.local"
|
|
||||||
- name: DB_PORT
|
|
||||||
value: "5432"
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "250m"
|
|
||||||
limits:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "500m"
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: authorization-service
|
|
||||||
spec:
|
|
||||||
type: LoadBalancer
|
|
||||||
selector:
|
|
||||||
app: authorization
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker Compose
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
|
||||||
authorization-1:
|
|
||||||
image: authorization:latest
|
|
||||||
environment:
|
|
||||||
- REDIS_HOST=redis
|
|
||||||
- REDIS_PORT=6379
|
|
||||||
- DB_HOST=postgres
|
|
||||||
- DB_PORT=5432
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
authorization-2:
|
|
||||||
image: authorization:latest
|
|
||||||
environment:
|
|
||||||
- REDIS_HOST=redis
|
|
||||||
- REDIS_PORT=6379
|
|
||||||
- DB_HOST=postgres
|
|
||||||
- DB_PORT=5432
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
authorization-3:
|
|
||||||
image: authorization:latest
|
|
||||||
environment:
|
|
||||||
- REDIS_HOST=redis
|
|
||||||
- REDIS_PORT=6379
|
|
||||||
- DB_HOST=postgres
|
|
||||||
- DB_PORT=5432
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
- postgres
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
command: redis-server --requirepass yourpassword
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: authorization
|
|
||||||
POSTGRES_USER: authuser
|
|
||||||
POSTGRES_PASSWORD: authpass
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
|
|
||||||
load-balancer:
|
|
||||||
image: nginx:alpine
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
|
||||||
depends_on:
|
|
||||||
- authorization-1
|
|
||||||
- authorization-2
|
|
||||||
- authorization-3
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx Load Balancer Config
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
upstream authorization {
|
|
||||||
least_conn;
|
|
||||||
server authorization-1:8080;
|
|
||||||
server authorization-2:8080;
|
|
||||||
server authorization-3:8080;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://authorization;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Redis Configuration
|
|
||||||
|
|
||||||
### Production Redis Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# redis.conf for production
|
|
||||||
maxmemory 2gb
|
|
||||||
maxmemory-policy allkeys-lru
|
|
||||||
requirepass your_strong_password_here
|
|
||||||
timeout 300
|
|
||||||
tcp-keepalive 60
|
|
||||||
|
|
||||||
# Persistence (optional)
|
|
||||||
save 900 1
|
|
||||||
save 300 10
|
|
||||||
save 60 10000
|
|
||||||
appendonly yes
|
|
||||||
appendfsync everysec
|
|
||||||
```
|
|
||||||
|
|
||||||
### Redis Cluster (High Availability)
|
|
||||||
|
|
||||||
For production, consider Redis Cluster or Sentinel:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Redis Cluster
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: redis-cluster-config
|
|
||||||
data:
|
|
||||||
redis.conf: |
|
|
||||||
cluster-enabled yes
|
|
||||||
cluster-config-file nodes.conf
|
|
||||||
cluster-node-timeout 5000
|
|
||||||
appendonly yes
|
|
||||||
maxmemory 2gb
|
|
||||||
maxmemory-policy allkeys-lru
|
|
||||||
```
|
|
||||||
|
|
||||||
## Monitoring and Observability
|
|
||||||
|
|
||||||
### Key Metrics to Track
|
|
||||||
|
|
||||||
1. **Cache Hit Rate**
|
|
||||||
|
|
||||||
- Monitor via `GetCacheStats()` endpoint
|
|
||||||
- Target: >95% hit rate for permissions
|
|
||||||
- Alert if drops below 90%
|
|
||||||
|
|
||||||
2. **Redis Availability**
|
|
||||||
|
|
||||||
- Monitor `distributed_cache` and `redis_available` fields
|
|
||||||
- Alert if Redis becomes unavailable
|
|
||||||
- System continues working (fail-open) but performance degrades
|
|
||||||
|
|
||||||
3. **Authorization Latency**
|
|
||||||
|
|
||||||
- Target: <50ms per authorization check
|
|
||||||
- Logs "WARN: Slow cached authorization" if exceeds threshold
|
|
||||||
- Track P50, P95, P99 latencies
|
|
||||||
|
|
||||||
4. **Instance Count**
|
|
||||||
- Monitor number of active instances
|
|
||||||
- Scale based on request rate
|
|
||||||
- Recommendation: 1 instance per 1000 req/s
|
|
||||||
|
|
||||||
### Prometheus Metrics (Recommended)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Add to your code
|
|
||||||
var (
|
|
||||||
cacheHits = prometheus.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "authz_cache_hits_total",
|
|
||||||
Help: "Total number of cache hits",
|
|
||||||
},
|
|
||||||
[]string{"cache_type"},
|
|
||||||
)
|
|
||||||
|
|
||||||
cacheMisses = prometheus.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "authz_cache_misses_total",
|
|
||||||
Help: "Total number of cache misses",
|
|
||||||
},
|
|
||||||
[]string{"cache_type"},
|
|
||||||
)
|
|
||||||
|
|
||||||
authzLatency = prometheus.NewHistogram(
|
|
||||||
prometheus.HistogramOpts{
|
|
||||||
Name: "authz_check_duration_seconds",
|
|
||||||
Help: "Authorization check latency",
|
|
||||||
Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Characteristics
|
|
||||||
|
|
||||||
### Throughput
|
|
||||||
|
|
||||||
| Setup | Instances | Expected RPS | Latency (P95) |
|
|
||||||
| --------------- | --------- | ------------ | ------------- |
|
|
||||||
| Single Instance | 1 | ~2,000 | <10ms |
|
|
||||||
| Small Cluster | 3 | ~6,000 | <15ms |
|
|
||||||
| Medium Cluster | 5 | ~10,000 | <20ms |
|
|
||||||
| Large Cluster | 10+ | ~20,000+ | <25ms |
|
|
||||||
|
|
||||||
> **Note:** Assumes Redis on same network, PostgreSQL optimized
|
|
||||||
|
|
||||||
### Cache Effectiveness
|
|
||||||
|
|
||||||
- **Permission Cache**: 99%+ hit rate (permissions rarely change)
|
|
||||||
- **Policy Cache**: 99%+ hit rate (policies rarely change)
|
|
||||||
- **User Attributes Cache**: 85-95% hit rate (depends on user count)
|
|
||||||
|
|
||||||
### Resource Requirements (Per Instance)
|
|
||||||
|
|
||||||
- **Memory**: 256MB base + (1KB × cached_users)
|
|
||||||
- **CPU**: 0.1 core idle, 0.5 core at 1000 req/s
|
|
||||||
- **Network**: Minimal (<1MB/s per 1000 req/s)
|
|
||||||
- **Redis Memory**: ~10KB per user + ~100KB for permissions/policies
|
|
||||||
|
|
||||||
## Scaling Guidelines
|
|
||||||
|
|
||||||
### When to Scale Up
|
|
||||||
|
|
||||||
1. **CPU utilization** consistently >70%
|
|
||||||
2. **Authorization latency** P95 >50ms
|
|
||||||
3. **Request rate** exceeds 2000 req/s per instance
|
|
||||||
4. **Memory usage** approaches 80% of limit
|
|
||||||
|
|
||||||
### When to Scale Down
|
|
||||||
|
|
||||||
1. **CPU utilization** consistently <20%
|
|
||||||
2. **Request rate** <500 req/s per instance
|
|
||||||
3. Cost optimization during off-peak hours
|
|
||||||
|
|
||||||
### Auto-scaling Rules (Kubernetes HPA)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: autoscaling/v2
|
|
||||||
kind: HorizontalPodAutoscaler
|
|
||||||
metadata:
|
|
||||||
name: authorization-hpa
|
|
||||||
spec:
|
|
||||||
scaleTargetRef:
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
name: authorization-service
|
|
||||||
minReplicas: 2
|
|
||||||
maxReplicas: 10
|
|
||||||
metrics:
|
|
||||||
- type: Resource
|
|
||||||
resource:
|
|
||||||
name: cpu
|
|
||||||
target:
|
|
||||||
type: Utilization
|
|
||||||
averageUtilization: 70
|
|
||||||
- type: Resource
|
|
||||||
resource:
|
|
||||||
name: memory
|
|
||||||
target:
|
|
||||||
type: Utilization
|
|
||||||
averageUtilization: 80
|
|
||||||
behavior:
|
|
||||||
scaleUp:
|
|
||||||
stabilizationWindowSeconds: 60
|
|
||||||
policies:
|
|
||||||
- type: Percent
|
|
||||||
value: 50
|
|
||||||
periodSeconds: 60
|
|
||||||
scaleDown:
|
|
||||||
stabilizationWindowSeconds: 300
|
|
||||||
policies:
|
|
||||||
- type: Percent
|
|
||||||
value: 25
|
|
||||||
periodSeconds: 60
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Horizontal Scalability
|
|
||||||
|
|
||||||
### Load Test with Multiple Instances
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Start 3 instances locally
|
|
||||||
docker-compose up -d --scale authorization=3
|
|
||||||
|
|
||||||
# Run load test
|
|
||||||
ab -n 10000 -c 100 http://localhost/v1/auth/check
|
|
||||||
|
|
||||||
# Monitor cache consistency
|
|
||||||
watch -n 1 'curl -s http://localhost/v1/cache/stats | jq'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify Cache Consistency
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
# Test cache synchronization across instances
|
|
||||||
|
|
||||||
INSTANCES=("http://instance1:8080" "http://instance2:8080" "http://instance3:8080")
|
|
||||||
|
|
||||||
# Trigger cache refresh on instance 1
|
|
||||||
curl -X POST ${INSTANCES[0]}/v1/admin/refresh-cache
|
|
||||||
|
|
||||||
# Wait for sync
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Check all instances have same data
|
|
||||||
for instance in "${INSTANCES[@]}"; do
|
|
||||||
echo "=== $instance ==="
|
|
||||||
curl -s $instance/v1/cache/stats | jq '.permissions_cached, .last_refresh'
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rollback Plan
|
|
||||||
|
|
||||||
If issues occur, you can temporarily disable Redis:
|
|
||||||
|
|
||||||
1. **Remove Redis environment variables**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
unset REDIS_HOST
|
|
||||||
unset REDIS_PASSWORD
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Service automatically falls back** to local cache
|
|
||||||
3. **No code changes required** - graceful degradation
|
|
||||||
4. **Authorization still works**, but instances are independent
|
|
||||||
|
|
||||||
## Migration Checklist
|
|
||||||
|
|
||||||
- [ ] Redis deployed and accessible
|
|
||||||
- [ ] Redis password configured
|
|
||||||
- [ ] Environment variables set (REDIS_HOST, REDIS_PORT, REDIS_PASSWORD)
|
|
||||||
- [ ] All instances can connect to Redis
|
|
||||||
- [ ] Load balancer configured
|
|
||||||
- [ ] Health checks passing (`/health`, `/ready`)
|
|
||||||
- [ ] Monitoring configured
|
|
||||||
- [ ] Load testing completed
|
|
||||||
- [ ] Cache hit rate verified (>90%)
|
|
||||||
- [ ] Latency within acceptable range (<50ms P95)
|
|
||||||
- [ ] Rollback plan documented and tested
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Issue: High Latency After Scaling
|
|
||||||
|
|
||||||
**Cause**: Redis network latency or insufficient resources
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check Redis latency
|
|
||||||
redis-cli --latency -h redis-host -p 6379
|
|
||||||
|
|
||||||
# If high, check Redis resources
|
|
||||||
redis-cli INFO stats | grep -E "instantaneous_ops_per_sec|used_memory"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue: Cache Misses on New Instances
|
|
||||||
|
|
||||||
**Cause**: New instances start with empty local cache
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
|
|
||||||
- Expected behavior, Redis cache is populated
|
|
||||||
- Local cache fills on first requests
|
|
||||||
- Monitor first 30 seconds after scaling
|
|
||||||
|
|
||||||
### Issue: Redis Connection Failures
|
|
||||||
|
|
||||||
**Cause**: Network issues, Redis overloaded, or password mismatch
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test Redis connectivity
|
|
||||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD PING
|
|
||||||
|
|
||||||
# Check service logs
|
|
||||||
kubectl logs -f deployment/authorization-service
|
|
||||||
|
|
||||||
# Look for: "ERROR: Rate limiter: Redis not available"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Your authorization microservice now supports:
|
|
||||||
|
|
||||||
✅ **Unlimited horizontal scaling** - Add instances without code changes
|
|
||||||
✅ **Shared cache state** - All instances see the same data
|
|
||||||
✅ **High availability** - Continues working if Redis fails
|
|
||||||
✅ **Low latency** - <50ms P95 authorization checks
|
|
||||||
✅ **Cost-effective** - Scale up/down based on demand
|
|
||||||
✅ **Production-ready** - Tested, monitored, and documented
|
|
||||||
|
|
||||||
**Next Steps**: Deploy to production and configure auto-scaling based on your traffic patterns.
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
# RBAC + ABAC Implementation Summary
|
|
||||||
|
|
||||||
## ✅ What Was Built
|
|
||||||
|
|
||||||
A complete **Role-Based Access Control (RBAC)** and **Attribute-Based Access Control (ABAC)** authorization system with:
|
|
||||||
|
|
||||||
### Core Components
|
|
||||||
|
|
||||||
1. **Data Models** (`models/rbac.go`)
|
|
||||||
|
|
||||||
- Permission, PolicyAttribute, UserAttribute, User
|
|
||||||
- AuthorizationContext, AuthorizationResult
|
|
||||||
|
|
||||||
2. **Database Repository** (`repository/permission_repository.go`)
|
|
||||||
|
|
||||||
- Permission lookup by resource + action
|
|
||||||
- Policy attributes retrieval
|
|
||||||
- User attributes retrieval
|
|
||||||
- Batch operations for caching
|
|
||||||
|
|
||||||
3. **Policy Evaluator** (`services/policy_evaluator.go`)
|
|
||||||
|
|
||||||
- ABAC policy evaluation engine
|
|
||||||
- 10 comparison operators (=, !=, >, <, >=, <=, IN, CONTAINS, etc.)
|
|
||||||
- Variable substitution (${resource.region})
|
|
||||||
- Attribute validation
|
|
||||||
|
|
||||||
4. **Authorization Service** (`services/authorize.go`)
|
|
||||||
|
|
||||||
- Main authorization logic
|
|
||||||
- Integrates repository and evaluator
|
|
||||||
- Performance monitoring
|
|
||||||
|
|
||||||
5. **Cached Service** (`services/cached_authorization.go`)
|
|
||||||
|
|
||||||
- High-performance caching layer
|
|
||||||
- 5-minute cache for permissions/policies
|
|
||||||
- LRU cache for user attributes
|
|
||||||
- Background refresh
|
|
||||||
|
|
||||||
6. **HTTP Handler** (`handlers/authorize.go`)
|
|
||||||
- REST API endpoint
|
|
||||||
- JWT integration
|
|
||||||
- Request validation
|
|
||||||
- Response formatting
|
|
||||||
|
|
||||||
## 🎯 Key Features
|
|
||||||
|
|
||||||
### RBAC
|
|
||||||
|
|
||||||
- Database-driven permissions
|
|
||||||
- Resource + Action based
|
|
||||||
- 27 permissions defined
|
|
||||||
|
|
||||||
### ABAC
|
|
||||||
|
|
||||||
- User attributes (region, role, action_user_role, etc.)
|
|
||||||
- Resource attributes (passed in request)
|
|
||||||
- Environment attributes (time, location, etc.)
|
|
||||||
- Dynamic policy evaluation
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
- **Without cache:** ~10-20ms per request
|
|
||||||
- **With cache:** ~0.5ms per request (200x faster)
|
|
||||||
- Cache hit rate: 98%+
|
|
||||||
- Supports 10M+ cached tokens
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- JWT authentication required
|
|
||||||
- User ID verification
|
|
||||||
- Audit trail ready
|
|
||||||
- Cache invalidation support
|
|
||||||
|
|
||||||
## 📊 Database Schema
|
|
||||||
|
|
||||||
```text
|
|
||||||
permissions (27 records)
|
|
||||||
├── id, permission_name, description
|
|
||||||
├── resource (users, cases, workload, etc.)
|
|
||||||
└── action (manage, view, encode, etc.)
|
|
||||||
|
|
||||||
policy_attributes (16 records)
|
|
||||||
├── attribute_name (role, region, action_user_role)
|
|
||||||
├── attribute_type (user, resource, environment)
|
|
||||||
├── comparison (=, !=, IN, CONTAINS, etc.)
|
|
||||||
├── attribute_value (Admin, ${resource.region}, etc.)
|
|
||||||
└── permission_id → permissions.id
|
|
||||||
|
|
||||||
user_attributes (14 records)
|
|
||||||
├── user_id → users.user_id
|
|
||||||
├── attribute_name (region, role, is_supervisor)
|
|
||||||
└── attribute_value (01, Admin, Y)
|
|
||||||
|
|
||||||
users (4 records)
|
|
||||||
└── user_id, first_name, last_name, role_id, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 Authorization Flow
|
|
||||||
|
|
||||||
```text
|
|
||||||
1. Client Request
|
|
||||||
↓
|
|
||||||
2. JWT Middleware (validates token)
|
|
||||||
↓
|
|
||||||
3. Authorization Handler
|
|
||||||
↓
|
|
||||||
4. Cached Authorization Service
|
|
||||||
↓
|
|
||||||
├─→ [CACHE HIT] Return cached result (0.5ms)
|
|
||||||
└─→ [CACHE MISS]
|
|
||||||
├─→ Get permission (resource + action)
|
|
||||||
├─→ Get user attributes
|
|
||||||
├─→ Get policy attributes
|
|
||||||
├─→ Evaluate policies (ABAC)
|
|
||||||
├─→ Cache result
|
|
||||||
└─→ Return decision (10-20ms)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🧪 Testing Examples
|
|
||||||
|
|
||||||
### Example 1: Admin Access ✅
|
|
||||||
|
|
||||||
```json
|
|
||||||
POST /v1/auth/check
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "users",
|
|
||||||
"action": "manage"
|
|
||||||
}
|
|
||||||
→ ALLOWED (user.role = Admin)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 2: Regional Access ✅
|
|
||||||
|
|
||||||
```json
|
|
||||||
POST /v1/auth/check
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "personnel",
|
|
||||||
"action": "assign_role",
|
|
||||||
"resource_data": {"region": "01"}
|
|
||||||
}
|
|
||||||
→ ALLOWED (user.region = resource.region)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 3: Role Restriction ❌
|
|
||||||
|
|
||||||
```json
|
|
||||||
POST /v1/auth/check
|
|
||||||
{
|
|
||||||
"user_id": "U0000000002",
|
|
||||||
"resource": "cases",
|
|
||||||
"action": "verify"
|
|
||||||
}
|
|
||||||
→ DENIED (Data Collector cannot verify)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 4: Role Inclusion ✅
|
|
||||||
|
|
||||||
```json
|
|
||||||
POST /v1/auth/check
|
|
||||||
{
|
|
||||||
"user_id": "U0000000003",
|
|
||||||
"resource": "data_processing",
|
|
||||||
"action": "certify"
|
|
||||||
}
|
|
||||||
→ ALLOWED (Provincial Focal Person in RFP,PFP)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 Files Created/Modified
|
|
||||||
|
|
||||||
### New Files
|
|
||||||
|
|
||||||
- `models/rbac.go` - RBAC/ABAC data models
|
|
||||||
- `repository/permission_repository.go` - Database layer
|
|
||||||
- `services/policy_evaluator.go` - ABAC engine
|
|
||||||
- `services/authorize.go` - Authorization service
|
|
||||||
- `services/cached_authorization.go` - Caching layer
|
|
||||||
- `docs/RBAC_ABAC_README.md` - Full documentation
|
|
||||||
- `docs/test_examples.txt` - Test cases
|
|
||||||
- `docs/database_schema.sql` - Schema reference
|
|
||||||
|
|
||||||
### Modified Files
|
|
||||||
|
|
||||||
- `handlers/authorize.go` - Updated handler
|
|
||||||
- `main.go` - Initialize auth service
|
|
||||||
|
|
||||||
## 🚀 Deployment Checklist
|
|
||||||
|
|
||||||
1. ✅ Database tables exist (permissions, policy_attributes, user_attributes, users)
|
|
||||||
2. ✅ Data populated in tables
|
|
||||||
3. ✅ JWT_KEY environment variable set
|
|
||||||
4. ✅ Database credentials configured
|
|
||||||
5. ✅ Go build successful
|
|
||||||
6. ✅ Test with sample requests
|
|
||||||
|
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
```bash
|
|
||||||
JWT_KEY=your_secret_key_here
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_PORT=3306
|
|
||||||
DB_USER=your_db_user
|
|
||||||
DB_PASSWORD=your_db_password
|
|
||||||
DB_NAME=your_database_name
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cache Settings (tunable in code)
|
|
||||||
|
|
||||||
```go
|
|
||||||
cacheExpiry: 5 * time.Minute // Permission/policy cache
|
|
||||||
userAttrLimit: 10000 // User attribute cache size
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📈 Performance Benchmarks
|
|
||||||
|
|
||||||
| Operation | Without Cache | With Cache |
|
|
||||||
| ----------------- | ------------- | -------------- |
|
|
||||||
| Permission lookup | 5-10ms | 0.1ms |
|
|
||||||
| Policy fetch | 3-5ms | 0.1ms |
|
|
||||||
| User attributes | 2-4ms | 0.1ms (cached) |
|
|
||||||
| **Total** | **10-20ms** | **0.5ms** |
|
|
||||||
|
|
||||||
### Load Testing Results
|
|
||||||
|
|
||||||
- **1000 req/sec:** Avg 0.5ms response
|
|
||||||
- **10,000 req/sec:** Avg 2ms response
|
|
||||||
- **Cache hit rate:** 98.5%
|
|
||||||
- **Memory usage:** ~50MB (10k cached users)
|
|
||||||
|
|
||||||
## 🛡️ Security Features
|
|
||||||
|
|
||||||
1. **JWT Required** - All endpoints protected
|
|
||||||
2. **User Verification** - Request user_id must match JWT
|
|
||||||
3. **Attribute Validation** - Type-safe attribute evaluation
|
|
||||||
4. **SQL Injection Protection** - Parameterized queries
|
|
||||||
5. **Cache Poisoning Prevention** - Atomic cache updates
|
|
||||||
|
|
||||||
## 📝 Adding New Permissions
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Step 1: Add permission
|
|
||||||
INSERT INTO permissions (permission_name, description, resource, action)
|
|
||||||
VALUES ('New Permission', 'Description', 'resource_name', 'action_name');
|
|
||||||
|
|
||||||
-- Step 2: Add policies (optional)
|
|
||||||
INSERT INTO policy_attributes
|
|
||||||
(attribute_name, attribute_type, comparison, attribute_value, permission_id)
|
|
||||||
VALUES
|
|
||||||
('role', 'user', '=', 'Admin', LAST_INSERT_ID());
|
|
||||||
|
|
||||||
-- Step 3: Wait 5 minutes or restart service for cache refresh
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Issue: Permission Not Found
|
|
||||||
|
|
||||||
**Solution:** Check permissions table, verify resource/action spelling
|
|
||||||
|
|
||||||
### Issue: Policy Fails
|
|
||||||
|
|
||||||
**Solution:** Verify user has required attributes in user_attributes table
|
|
||||||
|
|
||||||
### Issue: Slow Response
|
|
||||||
|
|
||||||
**Solution:** Check database indexes, monitor cache hit rate
|
|
||||||
|
|
||||||
### Issue: Cache Not Refreshing
|
|
||||||
|
|
||||||
**Solution:** Cache refreshes every 5 minutes automatically
|
|
||||||
|
|
||||||
## 🎓 Learning Resources
|
|
||||||
|
|
||||||
- Read `docs/RBAC_ABAC_README.md` for detailed documentation
|
|
||||||
- Check `docs/test_examples.txt` for test scenarios
|
|
||||||
- Review `docs/database_schema.sql` for schema details
|
|
||||||
|
|
||||||
## ✨ Next Steps
|
|
||||||
|
|
||||||
1. **Add Audit Logging** - Log all authorization decisions
|
|
||||||
2. **Add Metrics Endpoint** - Expose cache statistics
|
|
||||||
3. **Add Admin UI** - Manage permissions via web interface
|
|
||||||
4. **Add Batch Authorization** - Check multiple permissions at once
|
|
||||||
5. **Add Time-based Policies** - Environment.time policies
|
|
||||||
6. **Add IP-based Policies** - Environment.ip_address policies
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
# Quick Start Guide - RBAC + ABAC Authorization
|
|
||||||
|
|
||||||
## 🚀 Getting Started in 5 Minutes
|
|
||||||
|
|
||||||
### Step 1: Verify Database Setup
|
|
||||||
|
|
||||||
Your database already has the required tables and data:
|
|
||||||
|
|
||||||
- ✅ `permissions` (27 permissions)
|
|
||||||
- ✅ `policy_attributes` (16 policies)
|
|
||||||
- ✅ `user_attributes` (14 attributes)
|
|
||||||
- ✅ `users` (4 users)
|
|
||||||
|
|
||||||
### Step 2: Build & Run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd c:\Projects\UESS\Authorization
|
|
||||||
go build -o authorization.exe
|
|
||||||
./authorization.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Test Authorization
|
|
||||||
|
|
||||||
#### Test 1: Admin Can Manage Users ✅
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/v1/auth/check \
|
|
||||||
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"user_id\":\"U0000000001\",\"resource\":\"users\",\"action\":\"manage\",\"resource_data\":{}}"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Expected Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"allowed": true,
|
|
||||||
"reason": "All policies satisfied",
|
|
||||||
"permission_id": 1,
|
|
||||||
"evaluated_at": "2025-12-09T...",
|
|
||||||
"matched_policies": [1]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Test 2: Regional Access Control ✅
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/v1/auth/check \
|
|
||||||
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"user_id\":\"U0000000001\",\"resource\":\"personnel\",\"action\":\"assign_role\",\"resource_data\":{\"region\":\"01\"}}"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Expected Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"allowed": true,
|
|
||||||
"reason": "All policies satisfied",
|
|
||||||
"permission_id": 3,
|
|
||||||
"evaluated_at": "2025-12-09T...",
|
|
||||||
"matched_policies": [8]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Test 3: Data Collector Cannot Verify ❌
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/v1/auth/check \
|
|
||||||
-H "Authorization: Bearer DATA_COLLECTOR_JWT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"user_id\":\"U0000000002\",\"resource\":\"cases\",\"action\":\"verify\",\"resource_data\":{}}"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Expected Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"allowed": false,
|
|
||||||
"reason": "Policy failed: action_user_role != Data Collector...",
|
|
||||||
"permission_id": 14,
|
|
||||||
"evaluated_at": "2025-12-09T..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 Common Use Cases
|
|
||||||
|
|
||||||
### Check if User Can Perform Action
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// JavaScript/Frontend Example
|
|
||||||
const checkPermission = async (resource, action, resourceData = {}) => {
|
|
||||||
const response = await fetch("/v1/auth/check", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${jwtToken}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
user_id: currentUser.id,
|
|
||||||
resource: resource,
|
|
||||||
action: action,
|
|
||||||
resource_data: resourceData,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
return result.allowed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
if (await checkPermission("users", "manage")) {
|
|
||||||
// Show admin interface
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await checkPermission("cases", "verify", { region: userRegion })) {
|
|
||||||
// Allow case verification
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Go Backend Integration
|
|
||||||
|
|
||||||
```go
|
|
||||||
// In your service/handler
|
|
||||||
import "authorization/services"
|
|
||||||
|
|
||||||
func (s *MyService) PerformAction(userID, resource, action string) error {
|
|
||||||
ctx := &models.AuthorizationContext{
|
|
||||||
UserID: userID,
|
|
||||||
Resource: resource,
|
|
||||||
Action: action,
|
|
||||||
ResourceData: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := authService.Authorize(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("authorization failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !result.Allowed {
|
|
||||||
return fmt.Errorf("access denied: %s", result.Reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proceed with action
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔑 JWT Token Format
|
|
||||||
|
|
||||||
Your JWT should include these claims:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"email_address": "darrel.israel@example.com",
|
|
||||||
"role_id": "SuperAdmin",
|
|
||||||
"exp": 1702123456
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Permission Reference
|
|
||||||
|
|
||||||
### Most Common Permissions
|
|
||||||
|
|
||||||
| Resource | Action | Description | Policy |
|
|
||||||
| --------------- | -------------- | ------------------------- | ------------------ |
|
|
||||||
| users | manage | Create/edit user accounts | role = Admin |
|
|
||||||
| users | view | View user profiles | (no policy) |
|
|
||||||
| personnel | assign_role | Assign project roles | region match |
|
|
||||||
| personnel | assign_project | Grant project access | region match |
|
|
||||||
| workload | assign | Assign cases to staff | region match |
|
|
||||||
| workload | download | Download assigned cases | (no policy) |
|
|
||||||
| cases | encode | Create/update survey data | (no policy) |
|
|
||||||
| cases | verify | Review submitted data | NOT Data Collector |
|
|
||||||
| cases | return | Reject and return case | NOT Data Collector |
|
|
||||||
| data_processing | certify | Mark data as certified | RFP or PFP only |
|
|
||||||
|
|
||||||
## 🎯 Policy Examples
|
|
||||||
|
|
||||||
### Simple Role Check
|
|
||||||
|
|
||||||
```text
|
|
||||||
Policy: user.role = Admin
|
|
||||||
→ Only users with role="Admin" allowed
|
|
||||||
```
|
|
||||||
|
|
||||||
### Regional Matching
|
|
||||||
|
|
||||||
```text
|
|
||||||
Policy: user.region = ${resource.region}
|
|
||||||
→ User can only access resources in their region
|
|
||||||
```
|
|
||||||
|
|
||||||
### Role Exclusion
|
|
||||||
|
|
||||||
```text
|
|
||||||
Policy: user.action_user_role != Data Collector
|
|
||||||
→ Everyone except Data Collectors allowed
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Role Inclusion
|
|
||||||
|
|
||||||
```text
|
|
||||||
Policy: user.action_user_role IN RFP,PFP
|
|
||||||
→ Only RFP or PFP roles allowed
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Troubleshooting
|
|
||||||
|
|
||||||
### "Permission not found"
|
|
||||||
|
|
||||||
- Check if permission exists in database
|
|
||||||
- Verify resource and action names match exactly
|
|
||||||
- Wait 5 minutes for cache refresh or restart service
|
|
||||||
|
|
||||||
### "User attribute 'xxx' not found"
|
|
||||||
|
|
||||||
- Check `user_attributes` table for the user
|
|
||||||
- Ensure attribute name matches policy requirement
|
|
||||||
- Add missing attribute to database
|
|
||||||
|
|
||||||
### "Policy failed: region = 01 (actual: 03)"
|
|
||||||
|
|
||||||
- User's region doesn't match resource region
|
|
||||||
- Check user_attributes.region for the user
|
|
||||||
- Verify resource_data.region in request
|
|
||||||
|
|
||||||
## 📈 Performance Tips
|
|
||||||
|
|
||||||
1. **Cache Hit Rate**: ~98% with default settings
|
|
||||||
2. **Response Time**: <1ms for cached requests
|
|
||||||
3. **Memory Usage**: ~50MB for 10k users
|
|
||||||
4. **Cache Refresh**: Every 5 minutes automatically
|
|
||||||
|
|
||||||
## 🎓 Next Steps
|
|
||||||
|
|
||||||
1. Read full documentation: `docs/RBAC_ABAC_README.md`
|
|
||||||
2. Review test examples: `docs/test_examples.txt`
|
|
||||||
3. Check implementation details: `docs/IMPLEMENTATION_SUMMARY.md`
|
|
||||||
4. View database schema: `docs/database_schema.sql`
|
|
||||||
|
|
||||||
## 💡 Pro Tips
|
|
||||||
|
|
||||||
- **Frontend**: Check permissions before rendering UI elements
|
|
||||||
- **Backend**: Always verify permissions in backend (don't trust frontend)
|
|
||||||
- **Caching**: Service caches permissions automatically
|
|
||||||
- **Invalidation**: Call `InvalidateUserCache(userID)` after updating user attributes
|
|
||||||
- **Monitoring**: Add performance logging for slow authorization checks (>50ms)
|
|
||||||
|
|
||||||
## 🆘 Need Help?
|
|
||||||
|
|
||||||
Common issues and solutions:
|
|
||||||
|
|
||||||
1. **401 Unauthorized**: Check JWT token is valid and not expired
|
|
||||||
2. **403 Forbidden**: User lacks required permission or fails policy
|
|
||||||
3. **500 Internal Error**: Check database connection and logs
|
|
||||||
4. **Slow responses**: Check database indexes, cache hit rate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**You're all set!** 🎉
|
|
||||||
|
|
||||||
The authorization service is production-ready with RBAC + ABAC, high-performance caching, and comprehensive policy evaluation.
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
# RBAC + ABAC Authorization Service
|
|
||||||
|
|
||||||
Complete Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) implementation for the UESS Authorization microservice.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- ✅ **Database-driven permissions** - Permissions stored in MySQL
|
|
||||||
- ✅ **Policy-based authorization** - ABAC policies with attribute constraints
|
|
||||||
- ✅ **High-performance caching** - 5-minute cache for permissions and policies
|
|
||||||
- ✅ **User attribute support** - Dynamic user attributes for fine-grained control
|
|
||||||
- ✅ **Variable substitution** - Dynamic policy values (e.g., `${resource.region}`)
|
|
||||||
- ✅ **Multiple comparison operators** - =, !=, >, <, >=, <=, IN, CONTAINS
|
|
||||||
- ✅ **JWT integration** - Works with existing JWT middleware
|
|
||||||
|
|
||||||
## Database Schema
|
|
||||||
|
|
||||||
### Tables Created
|
|
||||||
|
|
||||||
1. **permissions** - System permissions (resource + action)
|
|
||||||
2. **policy_attributes** - ABAC policy constraints
|
|
||||||
3. **user_attributes** - User-specific attributes
|
|
||||||
4. **users** - User information
|
|
||||||
|
|
||||||
## API Usage
|
|
||||||
|
|
||||||
### Authorization Request
|
|
||||||
|
|
||||||
**Endpoint:** `POST /v1/auth/check`
|
|
||||||
|
|
||||||
**Headers:**
|
|
||||||
|
|
||||||
```http
|
|
||||||
Authorization: Bearer <JWT_TOKEN>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Request Body:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "users",
|
|
||||||
"action": "manage",
|
|
||||||
"resource_data": {
|
|
||||||
"region": "01"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Success Response (200):**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"allowed": true,
|
|
||||||
"reason": "All policies satisfied",
|
|
||||||
"permission_id": 1,
|
|
||||||
"evaluated_at": "2025-12-09T10:30:00Z",
|
|
||||||
"matched_policies": [1, 7]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Denied Response (403):**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"allowed": false,
|
|
||||||
"reason": "Policy failed: region = 01 (actual: 03)",
|
|
||||||
"permission_id": 1,
|
|
||||||
"evaluated_at": "2025-12-09T10:30:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Authorization Flow
|
|
||||||
|
|
||||||
1. **JWT Validation** - JWT middleware extracts user claims
|
|
||||||
2. **Permission Lookup** - Find permission by resource + action
|
|
||||||
3. **User Attributes** - Load user attributes from database (cached)
|
|
||||||
4. **Policy Evaluation** - Evaluate all ABAC policies
|
|
||||||
5. **Decision** - Return allowed/denied with reason
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Example 1: Admin Access (Permission ID: 1)
|
|
||||||
|
|
||||||
```text
|
|
||||||
Permission: users + manage
|
|
||||||
Policy: user.role = Admin
|
|
||||||
Result: Only users with role="Admin" can manage users
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 2: Regional Access (Permission ID: 3)
|
|
||||||
|
|
||||||
```text
|
|
||||||
Permission: personnel + assign_role
|
|
||||||
Policy: user.region = ${resource.region}
|
|
||||||
Result: User can only assign roles in their own region
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 3: Role Exclusion (Permission ID: 14)
|
|
||||||
|
|
||||||
```text
|
|
||||||
Permission: cases + verify
|
|
||||||
Policy: user.action_user_role != Data Collector
|
|
||||||
Result: Data Collectors cannot verify cases
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 4: Role Inclusion (Permission ID: 20)
|
|
||||||
|
|
||||||
```text
|
|
||||||
Permission: data_processing + certify
|
|
||||||
Policy: user.action_user_role IN RFP,PFP
|
|
||||||
Result: Only RFP or PFP can certify data
|
|
||||||
```
|
|
||||||
|
|
||||||
## Policy Comparison Operators
|
|
||||||
|
|
||||||
| Operator | Description | Example |
|
|
||||||
| ------------- | ------------------ | ------------------------------ |
|
|
||||||
| `=` | Equals | `user.role = Admin` |
|
|
||||||
| `!=` | Not equals | `user.role != Guest` |
|
|
||||||
| `>` | Greater than | `user.level > 5` |
|
|
||||||
| `<` | Less than | `user.age < 65` |
|
|
||||||
| `>=` | Greater or equal | `user.score >= 80` |
|
|
||||||
| `<=` | Less or equal | `user.attempts <= 3` |
|
|
||||||
| `IN` | In list | `user.role IN Admin,Moderator` |
|
|
||||||
| `CONTAINS` | Contains substring | `user.email CONTAINS @psa.gov` |
|
|
||||||
| `STARTS_WITH` | Starts with | `user.id STARTS_WITH U00` |
|
|
||||||
| `ENDS_WITH` | Ends with | `user.email ENDS_WITH .gov` |
|
|
||||||
|
|
||||||
## Variable Substitution
|
|
||||||
|
|
||||||
Policies support dynamic values using `${type.attribute}` syntax:
|
|
||||||
|
|
||||||
```text
|
|
||||||
${user.region} → User's region attribute
|
|
||||||
${resource.region} → Resource region from request
|
|
||||||
${environment.time} → Current timestamp
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```text
|
|
||||||
Policy: user.region = ${resource.region}
|
|
||||||
Evaluates to: "01" = "01" ✓
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### Without Caching
|
|
||||||
|
|
||||||
- Permission lookup: ~5-10ms
|
|
||||||
- Policy fetch: ~3-5ms
|
|
||||||
- **Total: ~10-20ms per request**
|
|
||||||
|
|
||||||
### With Caching
|
|
||||||
|
|
||||||
- Cache hit: ~0.1-0.5ms
|
|
||||||
- Cache miss: ~10-20ms (then cached)
|
|
||||||
- **Average: ~0.5ms per request** (98% cache hit rate)
|
|
||||||
|
|
||||||
### Cache Settings
|
|
||||||
|
|
||||||
- Permissions: Refreshed every 5 minutes
|
|
||||||
- Policies: Refreshed every 5 minutes
|
|
||||||
- User attributes: Cached indefinitely, LRU eviction at 10k entries
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Test Admin Access
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/v1/auth/check \
|
|
||||||
-H "Authorization: Bearer <JWT_TOKEN>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "users",
|
|
||||||
"action": "manage",
|
|
||||||
"resource_data": {}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Regional Access
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8080/v1/auth/check \
|
|
||||||
-H "Authorization: Bearer <JWT_TOKEN>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"user_id": "U0000000002",
|
|
||||||
"resource": "personnel",
|
|
||||||
"action": "assign_role",
|
|
||||||
"resource_data": {
|
|
||||||
"region": "01"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```text
|
|
||||||
authorization/
|
|
||||||
├── models/
|
|
||||||
│ ├── rbac.go # RBAC/ABAC data models
|
|
||||||
│ └── authorize.go # Request/Response models
|
|
||||||
├── repository/
|
|
||||||
│ └── permission_repository.go # Database access layer
|
|
||||||
├── services/
|
|
||||||
│ ├── authorize.go # Base authorization service
|
|
||||||
│ ├── policy_evaluator.go # ABAC policy evaluation engine
|
|
||||||
│ └── cached_authorization.go # Cached authorization service
|
|
||||||
├── handlers/
|
|
||||||
│ └── authorize.go # HTTP handler
|
|
||||||
└── middleware/
|
|
||||||
└── jwt.go # JWT authentication
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding New Permissions
|
|
||||||
|
|
||||||
1. **Add permission to database:**
|
|
||||||
|
|
||||||
```sql
|
|
||||||
INSERT INTO permissions (permission_name, description, resource, action)
|
|
||||||
VALUES ('Export Data', 'Export survey data to Excel', 'data', 'export');
|
|
||||||
```
|
|
||||||
|
|
||||||
1. **Add policies (optional):**
|
|
||||||
|
|
||||||
```sql
|
|
||||||
INSERT INTO policy_attributes (attribute_name, attribute_type, comparison, attribute_value, permission_id)
|
|
||||||
VALUES ('role', 'user', '=', 'Admin', 28);
|
|
||||||
```
|
|
||||||
|
|
||||||
1. **Cache updates automatically in 5 minutes** or restart service
|
|
||||||
|
|
||||||
## Cache Statistics
|
|
||||||
|
|
||||||
Get cache performance metrics (add this endpoint if needed):
|
|
||||||
|
|
||||||
```go
|
|
||||||
// GET /v1/auth/cache-stats
|
|
||||||
{
|
|
||||||
"permissions_cached": 27,
|
|
||||||
"policies_cached": 16,
|
|
||||||
"user_attributes_cached": 1523,
|
|
||||||
"last_refresh": "2025-12-09T10:25:00Z",
|
|
||||||
"cache_age_seconds": 245.3
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
1. **JWT Required** - All requests must have valid JWT
|
|
||||||
2. **User ID Verification** - Request user_id must match JWT user_id
|
|
||||||
3. **Secure Database** - Use read-only DB user for authorization service
|
|
||||||
4. **Audit Logging** - Log all authorization decisions (implement as needed)
|
|
||||||
5. **Cache Invalidation** - Invalidate user cache on attribute changes
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Permission Not Found
|
|
||||||
|
|
||||||
- Verify permission exists in database
|
|
||||||
- Check resource and action spelling
|
|
||||||
- Wait for cache refresh or restart service
|
|
||||||
|
|
||||||
### Policy Fails Unexpectedly
|
|
||||||
|
|
||||||
- Check user has required attributes in `user_attributes` table
|
|
||||||
- Verify attribute names match exactly
|
|
||||||
- Check comparison operator is correct
|
|
||||||
- Use variable substitution syntax correctly
|
|
||||||
|
|
||||||
### Slow Performance
|
|
||||||
|
|
||||||
- Check database indexes on permissions table
|
|
||||||
- Monitor cache hit rate
|
|
||||||
- Increase cache expiry if permissions change rarely
|
|
||||||
- Check database connection pool settings
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
-- Example Authorization Requests for Testing
|
|
||||||
|
|
||||||
-- 1. Admin Managing Users (Should succeed for U0000000001)
|
|
||||||
-- Permission: Manage User Accounts (ID: 1)
|
|
||||||
-- Policy: user.role = Admin
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "users",
|
|
||||||
"action": "manage",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (user has role="Super Admin")
|
|
||||||
|
|
||||||
-- 2. Regional Permission Assignment (Should succeed for same region)
|
|
||||||
-- Permission: Assign Project Roles (ID: 3)
|
|
||||||
-- Policy: user.region = ${resource.region}
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "personnel",
|
|
||||||
"action": "assign_role",
|
|
||||||
"resource_data": {
|
|
||||||
"region": "01"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (user region "01" matches resource region "01")
|
|
||||||
|
|
||||||
-- 3. Regional Permission Assignment (Should fail for different region)
|
|
||||||
-- Permission: Assign Project Roles (ID: 3)
|
|
||||||
-- Policy: user.region = ${resource.region}
|
|
||||||
{
|
|
||||||
"user_id": "U0000000003",
|
|
||||||
"resource": "personnel",
|
|
||||||
"action": "assign_role",
|
|
||||||
"resource_data": {
|
|
||||||
"region": "01"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-- Expected: DENIED (user region "03" doesn't match resource region "01")
|
|
||||||
|
|
||||||
-- 4. Data Collector Cannot Verify Cases
|
|
||||||
-- Permission: Verify Case (ID: 14)
|
|
||||||
-- Policy: user.action_user_role != Data Collector
|
|
||||||
{
|
|
||||||
"user_id": "U0000000002",
|
|
||||||
"resource": "cases",
|
|
||||||
"action": "verify",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: DENIED (user is Data Collector)
|
|
||||||
|
|
||||||
-- 5. Certify Data (RFP/PFP only)
|
|
||||||
-- Permission: Certify Data (ID: 20)
|
|
||||||
-- Policy: user.action_user_role IN RFP,PFP
|
|
||||||
{
|
|
||||||
"user_id": "U0000000003",
|
|
||||||
"resource": "data_processing",
|
|
||||||
"action": "certify",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (user is Provincial Focal Person)
|
|
||||||
|
|
||||||
-- 6. Certify Data (Should fail for non-RFP/PFP)
|
|
||||||
-- Permission: Certify Data (ID: 20)
|
|
||||||
-- Policy: user.action_user_role IN RFP,PFP
|
|
||||||
{
|
|
||||||
"user_id": "U0000000002",
|
|
||||||
"resource": "data_processing",
|
|
||||||
"action": "certify",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: DENIED (user is Data Collector, not RFP/PFP)
|
|
||||||
|
|
||||||
-- 7. View User Profiles (No policies - should succeed)
|
|
||||||
-- Permission: View User Profiles (ID: 2)
|
|
||||||
-- No policies defined
|
|
||||||
{
|
|
||||||
"user_id": "U0000000002",
|
|
||||||
"resource": "users",
|
|
||||||
"action": "view",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (no policies to fail)
|
|
||||||
|
|
||||||
-- 8. DPS Role Validation
|
|
||||||
-- Permission: Validate Data (ID: 18)
|
|
||||||
-- Policy: user.role_dps = 1
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "data_processing",
|
|
||||||
"action": "validate",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (user has role_dps=1)
|
|
||||||
|
|
||||||
-- 9. Multiple Policies - Regional Workload Assignment
|
|
||||||
-- Permission: Assign Workload (ID: 9)
|
|
||||||
-- Policy: user.region = ${resource.region}
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "workload",
|
|
||||||
"action": "assign",
|
|
||||||
"resource_data": {
|
|
||||||
"region": "01"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-- Expected: ALLOWED (user in region 01, resource in region 01)
|
|
||||||
|
|
||||||
-- 10. Permission Not Found (Should return appropriate error)
|
|
||||||
{
|
|
||||||
"user_id": "U0000000001",
|
|
||||||
"resource": "nonexistent",
|
|
||||||
"action": "delete",
|
|
||||||
"resource_data": {}
|
|
||||||
}
|
|
||||||
-- Expected: DENIED with "Permission not found" reason
|
|
||||||
Reference in New Issue
Block a user