Files
goplt/services/audit/internal/api/server.go
0x1d 31e8ca7ce9
Some checks failed
CI / Test (pull_request) Successful in 51s
CI / Lint (pull_request) Failing after 26s
CI / Build (pull_request) Successful in 38s
CI / Format Check (pull_request) Failing after 2s
fix(lint): use explicit safe type conversions for gosec
Use separate variables with explicit else branches to make type
conversions safe and satisfy gosec integer overflow checks.
2025-11-07 09:37:53 +01:00

134 lines
3.4 KiB
Go

// Package api provides gRPC server implementation for Audit Service.
package api
import (
"math"
"context"
auditv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/audit/v1"
"git.dcentral.systems/toolz/goplt/services/audit/internal/service"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Server implements the AuditService gRPC server.
type Server struct {
auditv1.UnimplementedAuditServiceServer
service *service.AuditService
logger *zap.Logger
}
// NewServer creates a new Audit Service gRPC server.
func NewServer(auditService *service.AuditService, logger *zap.Logger) *Server {
return &Server{
service: auditService,
logger: logger,
}
}
// Record records an audit log entry.
func (s *Server) Record(ctx context.Context, req *auditv1.RecordRequest) (*auditv1.RecordResponse, error) {
if req.Entry == nil {
return nil, status.Error(codes.InvalidArgument, "entry is required")
}
entry := req.Entry
// Convert proto entry to service entry
serviceEntry := &service.AuditLogEntry{
UserID: entry.UserId,
Action: entry.Action,
Resource: entry.Resource,
ResourceID: entry.ResourceId,
IPAddress: entry.IpAddress,
UserAgent: entry.UserAgent,
Metadata: entry.Metadata,
Timestamp: entry.Timestamp,
}
// Record the audit log
if err := s.service.Record(ctx, serviceEntry); err != nil {
s.logger.Error("Failed to record audit log",
zap.Error(err),
zap.String("user_id", entry.UserId),
zap.String("action", entry.Action),
)
return nil, status.Errorf(codes.Internal, "failed to record audit log: %v", err)
}
return &auditv1.RecordResponse{
Success: true,
}, nil
}
// Query queries audit logs based on filters.
func (s *Server) Query(ctx context.Context, req *auditv1.QueryRequest) (*auditv1.QueryResponse, error) {
// Convert proto filters to service filters
filters := &service.AuditLogFilters{
Limit: int(req.Limit),
Offset: int(req.Offset),
}
if req.UserId != nil {
userID := *req.UserId
filters.UserID = &userID
}
if req.Action != nil {
action := *req.Action
filters.Action = &action
}
if req.Resource != nil {
resource := *req.Resource
filters.Resource = &resource
}
if req.ResourceId != nil {
resourceID := *req.ResourceId
filters.ResourceID = &resourceID
}
if req.StartTime != nil {
startTime := *req.StartTime
filters.StartTime = &startTime
}
if req.EndTime != nil {
endTime := *req.EndTime
filters.EndTime = &endTime
}
// Query audit logs
entries, err := s.service.Query(ctx, filters)
if err != nil {
s.logger.Error("Failed to query audit logs",
zap.Error(err),
)
return nil, status.Errorf(codes.Internal, "failed to query audit logs: %v", err)
}
// Convert service entries to proto entries
protoEntries := make([]*auditv1.AuditLogEntry, 0, len(entries))
for _, entry := range entries {
protoEntries = append(protoEntries, &auditv1.AuditLogEntry{
UserId: entry.UserID,
Action: entry.Action,
Resource: entry.Resource,
ResourceId: entry.ResourceID,
IpAddress: entry.IPAddress,
UserAgent: entry.UserAgent,
Metadata: entry.Metadata,
Timestamp: entry.Timestamp,
})
}
total := len(protoEntries)
var totalInt32 int32
if total > math.MaxInt32 {
totalInt32 = math.MaxInt32
} else {
totalInt32 = int32(total)
}
return &auditv1.QueryResponse{
Entries: protoEntries,
Total: totalInt32, // Note: This is a simplified total, actual total would require a count query
}, nil
}