Skip to content

Commit 01adc10

Browse files
ginaxu1gemini-code-assist[bot]sthanikan2000
authored
#340 Part 3: Complete Audit Flow integration and create Audit Interface (#413)
* Add Audit Service to CE and PDP in OE federator * Update exchange/orchestration-engine/middleware/audit_middleware.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Refactor audit service enums and logging methods for improved clarity and consistency * Add CONSENT_CHECK to eventTypes in audit service enum configuration --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Thanikan <sthanikan2000@gmail.com>
1 parent 8b29986 commit 01adc10

File tree

32 files changed

+2066
-171
lines changed

32 files changed

+2066
-171
lines changed

audit-service/README.md

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Audit Service
22

3-
A Go microservice for centralized audit logging across OpenDIF services, providing distributed tracing and comprehensive event tracking.
3+
A generic Go microservice for centralized audit logging across distributed services, providing distributed tracing and comprehensive event tracking.
44

55
[![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)](https://golang.org/)
66
[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](../LICENSE)
@@ -115,6 +115,31 @@ Copy `.env.example` to `.env` and configure:
115115

116116
For PostgreSQL configuration and advanced settings, see [.env.example](.env.example).
117117

118+
### Event Type Configuration
119+
120+
Event types are configurable via `config/enums.yaml`. This allows you to customize the audit service for your specific use case. The service comes with generic default event types, but you can add project-specific ones.
121+
122+
**Default Event Types:**
123+
- `MANAGEMENT_EVENT` - Administrative operations
124+
- `USER_MANAGEMENT` - User-related operations
125+
- `DATA_FETCH` - Data retrieval operations
126+
127+
**Customizing Event Types:**
128+
129+
Edit `config/enums.yaml` to add your own event types:
130+
131+
```yaml
132+
enums:
133+
eventTypes:
134+
- MANAGEMENT_EVENT
135+
- USER_MANAGEMENT
136+
- DATA_FETCH
137+
- YOUR_CUSTOM_EVENT_TYPE
138+
- ANOTHER_EVENT_TYPE
139+
```
140+
141+
See [config/README.md](config/README.md) for detailed configuration options.
142+
118143
## API Endpoints
119144
120145
### Core Endpoints
@@ -136,13 +161,13 @@ curl -X POST http://localhost:3001/api/audit-logs \
136161
-d '{
137162
"traceId": "550e8400-e29b-41d4-a716-446655440000",
138163
"timestamp": "2024-01-20T10:00:00Z",
139-
"eventType": "POLICY_CHECK",
164+
"eventType": "MANAGEMENT_EVENT",
140165
"eventAction": "READ",
141166
"status": "SUCCESS",
142167
"actorType": "SERVICE",
143-
"actorId": "orchestration-engine",
168+
"actorId": "my-service",
144169
"targetType": "SERVICE",
145-
"targetId": "policy-decision-point"
170+
"targetId": "target-service"
146171
}'
147172
```
148173

@@ -156,18 +181,9 @@ curl http://localhost:3001/api/audit-logs
156181
curl http://localhost:3001/api/audit-logs?traceId=550e8400-e29b-41d4-a716-446655440000
157182
158183
# Filter by event type
159-
curl http://localhost:3001/api/audit-logs?eventType=POLICY_CHECK&status=SUCCESS
184+
curl http://localhost:3001/api/audit-logs?eventType=MANAGEMENT_EVENT&status=SUCCESS
160185
```
161186

162-
See [docs/API.md](docs/API.md) for complete API documentation.
163-
164-
## Documentation
165-
166-
- **[API Documentation](docs/API.md)** - Complete API reference with examples
167-
- **[Database Configuration](docs/DATABASE_CONFIGURATION.md)** - Database setup and configuration guide
168-
- **[Architecture](docs/ARCHITECTURE.md)** - Project structure and design patterns
169-
- **[OpenAPI Spec](openapi.yaml)** - OpenAPI 3.0 specification
170-
171187
## Development
172188

173189
### Project Structure
@@ -187,8 +203,6 @@ audit-service/
187203
└── main.go # Entry point
188204
```
189205
190-
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed architecture documentation.
191-
192206
### Running Tests
193207
194208
```bash
@@ -221,6 +235,49 @@ go build -o audit-service
221235
go build -ldflags="-X main.Version=1.0.0 -X main.GitCommit=$(git rev-parse HEAD)" -o audit-service
222236
```
223237

238+
## Integration
239+
240+
The Audit Service is designed to be integrated into any microservices architecture. It provides a simple REST API that can be called from any service.
241+
242+
### Integration Pattern
243+
244+
1. **Configure your service** to point to the audit service URL
245+
2. **Send audit events** via HTTP POST to `/api/audit-logs`
246+
3. **Query audit logs** via HTTP GET from `/api/audit-logs`
247+
248+
### Client Libraries
249+
250+
You can integrate the audit service using:
251+
252+
- **HTTP Client**: Direct HTTP calls to the REST API
253+
- **Shared Client Package**: Use the `shared/audit` package (if available in your project)
254+
- **Custom Wrapper**: Create your own client library
255+
256+
### Example Integration
257+
258+
```go
259+
// Example: Log an audit event from your service
260+
auditRequest := map[string]interface{}{
261+
"traceId": traceID,
262+
"timestamp": time.Now().UTC().Format(time.RFC3339),
263+
"eventType": "YOUR_EVENT_TYPE",
264+
"status": "SUCCESS",
265+
"actorType": "SERVICE",
266+
"actorId": "your-service-name",
267+
"targetType": "RESOURCE",
268+
"targetId": "resource-id",
269+
}
270+
271+
// POST to http://audit-service:3001/api/audit-logs
272+
```
273+
274+
### Graceful Degradation
275+
276+
- Services continue to function normally if audit service is unavailable
277+
- No errors are thrown when audit service URL is not configured
278+
- Audit operations should be asynchronous (fire-and-forget) to avoid blocking requests
279+
- Services can be started before audit service is ready
280+
224281
## Deployment
225282

226283
### Docker
@@ -243,13 +300,17 @@ docker run -d \
243300
-e DB_TYPE=postgres \
244301
-e DB_HOST=postgres \
245302
-e DB_PASSWORD=your_password \
303+
-e DB_NAME=audit_db \
246304
audit-service
247305
```
248306

249307
### Docker Compose
250308

309+
The audit service includes a `docker-compose.yml` for standalone deployment:
310+
251311
```bash
252-
# Start service
312+
# Deploy audit service
313+
cd audit-service
253314
docker compose up -d
254315

255316
# View logs
@@ -266,30 +327,8 @@ docker compose down
266327
3. **CORS**: Configure `CORS_ALLOWED_ORIGINS` appropriately
267328
4. **Monitoring**: Monitor service health via `/health` endpoint
268329
5. **Backup**: Implement database backup strategy
269-
270-
## Integration with OpenDIF Services
271-
272-
The Audit Service integrates with:
273-
274-
- **Orchestration Engine** - Tracks data exchange operations
275-
- **Portal Backend** - Logs administrative actions
276-
- **Consent Engine** - Records consent changes
277-
278-
Audit logging is **optional** - services function normally without it.
279-
280-
### Configuration in Other Services
281-
282-
```bash
283-
# Enable audit logging in orchestration-engine
284-
export AUDIT_SERVICE_ENABLED=true
285-
export AUDIT_SERVICE_URL=http://audit-service:3001
286-
287-
# Enable audit logging in portal-backend
288-
export AUDIT_SERVICE_ENABLED=true
289-
export AUDIT_SERVICE_URL=http://audit-service:3001
290-
```
291-
292-
See [../exchange/AUDIT_SERVICE.md](../exchange/AUDIT_SERVICE.md) for integration documentation.
330+
6. **High Availability**: Consider deploying multiple instances behind a load balancer
331+
7. **Security**: Implement authentication/authorization if exposing the service publicly
293332

294333
## Troubleshooting
295334

@@ -312,6 +351,12 @@ See [../exchange/AUDIT_SERVICE.md](../exchange/AUDIT_SERVICE.md) for integration
312351
- Check credentials and SSL settings
313352
- Verify network connectivity
314353

354+
**Event type validation errors:**
355+
356+
- Check that your event types are defined in `config/enums.yaml`
357+
- Verify the enum configuration file is being loaded correctly
358+
- Check service logs for validation error details
359+
315360
See [docs/DATABASE_CONFIGURATION.md](docs/DATABASE_CONFIGURATION.md) for detailed troubleshooting.
316361

317362
## Contributing
@@ -328,12 +373,4 @@ This project is licensed under the Apache License 2.0 - see [LICENSE](../LICENSE
328373

329374
## Support
330375

331-
- **Issues**: [GitHub Issues](https://github.com/OpenDIF/opendif-core/issues)
332-
- **Discussions**: [GitHub Discussions](https://github.com/OpenDIF/opendif-core/discussions)
333-
- **Documentation**: [OpenDIF Documentation](https://github.com/OpenDIF/opendif-core/tree/main/docs)
334-
335-
## Related Services
336-
337-
- [Orchestration Engine](../exchange/orchestration-engine/) - Data exchange orchestration
338-
- [Portal Backend](../portal-backend/) - Admin portal backend
339-
- [Consent Engine](../exchange/consent-engine/) - Consent management
376+
For issues, questions, or contributions, please use the project's issue tracker and discussion forums.

audit-service/config/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ type Config struct {
3333
}
3434

3535
// DefaultEnums provides default enum values if config file is not found
36+
// Note: OpenDIF-specific event types (ORCHESTRATION_REQUEST_RECEIVED, POLICY_CHECK, CONSENT_CHECK, PROVIDER_FETCH)
37+
// should be added to config/enums.yaml for project-specific configurations
3638
var DefaultEnums = AuditEnums{
3739
EventTypes: []string{
38-
"POLICY_CHECK",
3940
"MANAGEMENT_EVENT",
4041
"USER_MANAGEMENT",
4142
"DATA_FETCH",
42-
"CONSENT_CHECK",
4343
},
4444
EventActions: []string{
4545
"CREATE",

audit-service/config/enums.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ enums:
99
- POLICY_CHECK
1010
- MANAGEMENT_EVENT
1111
- USER_MANAGEMENT
12-
- DATA_FETCH
1312
- CONSENT_CHECK
13+
- DATA_REQUEST
14+
- PROVIDER_FETCH
1415

1516
# Event Action: CRUD operations
1617
eventActions:
@@ -29,6 +30,7 @@ enums:
2930
- ADMIN
3031
- MEMBER
3132
- SYSTEM
33+
- APPLICATION
3234

3335
# Target Type: Types of targets that actions can be performed on
3436
targetTypes:

audit-service/database/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func NewDatabaseConfig() *Config {
6868

6969
// For SQLite: only DB_TYPE=sqlite or DB_PATH count as configuration
7070
// DB_HOST is only relevant when DB_TYPE=postgres
71-
useFileBasedSQLite := dbPathSet || (dbTypeSet && dbTypeStr != "postgres" && dbTypeStr != "postgresql")
71+
useFileBasedSQLite := dbPathSet || (dbTypeSet && dbTypeStr != "postgres" && dbTypeStr != "postgresql")
7272

7373
switch dbTypeStr {
7474
case "postgres", "postgresql":
@@ -103,7 +103,7 @@ useFileBasedSQLite := dbPathSet || (dbTypeSet && dbTypeStr != "postgres" && dbTy
103103
config.MaxIdleConns = parseIntOrDefault("DB_MAX_IDLE_CONNS", 1)
104104

105105
// Determine database path based on configuration
106-
if !useFileBasedSQLite {
106+
if !useFileBasedSQLite {
107107
// No SQLite configuration at all → in-memory database for quick testing
108108
config.DatabasePath = ":memory:"
109109
slog.Info("No database configuration found, using in-memory SQLite")

audit-service/v1/database/gorm_repository.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func (r *GormRepository) GetAuditLogs(ctx context.Context, filters *AuditLogFilt
7777
}
7878

7979
// Apply pagination and ordering
80+
// Note: Results are ordered by timestamp DESC (newest first) for general queries.
81+
// For trace-specific queries, use GetAuditLogsByTraceID which orders by ASC (chronological).
8082
limit := filters.Limit
8183
if limit <= 0 {
8284
limit = 100 // default

audit-service/v1/models/audit_log.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package models
22

33
import (
4+
"database/sql/driver"
45
"encoding/json"
56
"fmt"
67
"sync"
@@ -11,6 +12,71 @@ import (
1112
"gorm.io/gorm"
1213
)
1314

15+
// JSONBRawMessage is a custom type that properly handles JSONB scanning from PostgreSQL
16+
// It implements sql.Scanner and driver.Valuer interfaces to handle both PostgreSQL JSONB
17+
// (which can return as string or []byte) and SQLite TEXT (which returns as []byte)
18+
// This type wraps json.RawMessage to provide database scanning capabilities while maintaining
19+
// the same JSON marshaling behavior as json.RawMessage.
20+
type JSONBRawMessage json.RawMessage
21+
22+
// Scan implements the sql.Scanner interface for JSONBRawMessage
23+
// Handles both PostgreSQL JSONB (string or []byte) and SQLite TEXT ([]byte)
24+
func (j *JSONBRawMessage) Scan(value interface{}) error {
25+
if value == nil {
26+
*j = JSONBRawMessage(nil)
27+
return nil
28+
}
29+
30+
var bytes []byte
31+
switch v := value.(type) {
32+
case []byte:
33+
bytes = v
34+
case string:
35+
bytes = []byte(v)
36+
default:
37+
return fmt.Errorf("cannot scan %T into JSONBRawMessage", value)
38+
}
39+
40+
// Note: We don't validate JSON here to avoid performance overhead.
41+
// The database should already contain valid JSON. If validation is needed,
42+
// it should be done at the application layer when unmarshaling.
43+
*j = JSONBRawMessage(bytes)
44+
return nil
45+
}
46+
47+
// Value implements the driver.Valuer interface for JSONBRawMessage
48+
func (j JSONBRawMessage) Value() (driver.Value, error) {
49+
if len(j) == 0 {
50+
return nil, nil
51+
}
52+
return []byte(j), nil
53+
}
54+
55+
// MarshalJSON implements json.Marshaler for JSONBRawMessage
56+
// Delegates to the underlying json.RawMessage behavior
57+
func (j JSONBRawMessage) MarshalJSON() ([]byte, error) {
58+
if len(j) == 0 {
59+
return []byte("null"), nil
60+
}
61+
return []byte(j), nil
62+
}
63+
64+
// UnmarshalJSON implements json.Unmarshaler for JSONBRawMessage
65+
// Delegates to the underlying json.RawMessage behavior
66+
func (j *JSONBRawMessage) UnmarshalJSON(data []byte) error {
67+
if j == nil {
68+
return fmt.Errorf("JSONBRawMessage: UnmarshalJSON on nil pointer")
69+
}
70+
*j = JSONBRawMessage(data)
71+
return nil
72+
}
73+
74+
// GormDataType returns the GORM data type for JSONBRawMessage
75+
// This helps GORM understand the database column type
76+
func (JSONBRawMessage) GormDataType() string {
77+
return "jsonb"
78+
}
79+
1480
// Audit log status constants (not configurable via YAML as they are core to the system)
1581
const (
1682
StatusSuccess = "SUCCESS"
@@ -66,9 +132,9 @@ type AuditLog struct {
66132
TargetID *string `gorm:"type:varchar(255)" json:"targetId,omitempty"` // resource_id or service_name
67133

68134
// Metadata (Payload without PII/sensitive data)
69-
RequestMetadata json.RawMessage `gorm:"type:text" json:"requestMetadata,omitempty"` // Request payload without PII/sensitive data
70-
ResponseMetadata json.RawMessage `gorm:"type:text" json:"responseMetadata,omitempty"` // Response or Error details
71-
AdditionalMetadata json.RawMessage `gorm:"type:text" json:"additionalMetadata,omitempty"` // Additional context-specific data
135+
RequestMetadata JSONBRawMessage `gorm:"type:jsonb" json:"requestMetadata,omitempty"` // Request payload without PII/sensitive data
136+
ResponseMetadata JSONBRawMessage `gorm:"type:jsonb" json:"responseMetadata,omitempty"` // Response or Error details
137+
AdditionalMetadata JSONBRawMessage `gorm:"type:jsonb" json:"additionalMetadata,omitempty"` // Additional context-specific data
72138

73139
// BaseModel provides CreatedAt
74140
BaseModel

audit-service/v1/models/request_dtos.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package models
22

3-
import (
4-
"encoding/json"
5-
)
6-
73
// CreateAuditLogRequest represents the request payload for creating a generalized audit log
84
// This matches the final SQL schema with unified actor/target approach
95
type CreateAuditLogRequest struct {
@@ -27,7 +23,9 @@ type CreateAuditLogRequest struct {
2723
TargetID *string `json:"targetId,omitempty"` // resource_id or service_name
2824

2925
// Metadata (Payload without PII/sensitive data)
30-
RequestMetadata json.RawMessage `json:"requestMetadata,omitempty"` // Request payload without PII/sensitive data
31-
ResponseMetadata json.RawMessage `json:"responseMetadata,omitempty"` // Response or Error details
32-
AdditionalMetadata json.RawMessage `json:"additionalMetadata,omitempty"` // Additional context-specific data
26+
// Using JSONBRawMessage instead of json.RawMessage to avoid type conversion
27+
// JSONBRawMessage implements json.Unmarshaler, so it works seamlessly with JSON decoding
28+
RequestMetadata JSONBRawMessage `json:"requestMetadata,omitempty"` // Request payload without PII/sensitive data
29+
ResponseMetadata JSONBRawMessage `json:"responseMetadata,omitempty"` // Response or Error details
30+
AdditionalMetadata JSONBRawMessage `json:"additionalMetadata,omitempty"` // Additional context-specific data
3331
}

0 commit comments

Comments
 (0)