-
Notifications
You must be signed in to change notification settings - Fork 116
/
audit.go
132 lines (109 loc) · 3.45 KB
/
audit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package audit
// <!-- START clutchdoc -->
// description: Returns audit event logs from the database.
// <!-- END clutchdoc -->
import (
"context"
"errors"
"fmt"
"strconv"
"time"
"github.com/golang/protobuf/ptypes/any"
"github.com/uber-go/tally/v4"
"go.uber.org/zap"
auditv1 "github.com/lyft/clutch/backend/api/audit/v1"
"github.com/lyft/clutch/backend/module"
"github.com/lyft/clutch/backend/service"
"github.com/lyft/clutch/backend/service/audit"
)
const Name = "clutch.module.audit"
func New(*any.Any, *zap.Logger, tally.Scope) (module.Module, error) {
auditClient, ok := service.Registry["clutch.service.audit"]
if !ok {
return nil, errors.New("could not find service")
}
c, ok := auditClient.(audit.Auditor)
if !ok {
return nil, errors.New("service was not the correct type")
}
mod := &mod{
client: c,
}
return mod, nil
}
type mod struct {
client audit.Auditor
}
func (m *mod) Register(r module.Registrar) error {
auditv1.RegisterAuditAPIServer(r.GRPCServer(), m)
return r.RegisterJSONGateway(auditv1.RegisterAuditAPIHandler)
}
func (m *mod) GetEvents(ctx context.Context, req *auditv1.GetEventsRequest) (*auditv1.GetEventsResponse, error) {
resp := &auditv1.GetEventsResponse{}
var start time.Time
var end *time.Time
switch req.GetWindow().(type) {
case *auditv1.GetEventsRequest_Range:
timerange := req.GetRange()
start = timerange.StartTime.AsTime()
endTime := timerange.EndTime.AsTime()
end = &endTime
case *auditv1.GetEventsRequest_Since:
if err := req.GetSince().CheckValid(); err != nil {
return nil, fmt.Errorf("problem parsing duration: %w", err)
}
window := req.GetSince().AsDuration()
start = time.Now().Add(-window)
default:
return nil, errors.New("no time window requested")
}
var page int
if req.PageToken != "" {
p, err := strconv.Atoi(req.PageToken)
if err != nil || p < 0 {
return nil, fmt.Errorf("invalid page token: %s", req.PageToken)
}
page = p
}
limit := int(req.Limit)
// if a request has no limit we return all found events.
if req.Limit == 0 {
// in the event there is no request limit set limit to be 1 for purposes
// of determining page offset later.
limit = 1
}
startIdx := page * limit
// n.b. the user defined limit is increased by 1 to help determine if there is
// a subsequent page of information that should be denoted in the response
additionalEventsNumber := int64(limit + 1)
options := &audit.ReadOptions{Offset: int64(startIdx)}
// if request limit is specified pass that value into the read options call
if req.Limit != 0 {
options.Limit = additionalEventsNumber
}
events, err := m.client.ReadEvents(ctx, start, end, options)
if err != nil {
return nil, err
}
// This logic is only used for purposes of pagination and should only be triggered if:
// 1. there are more than 0 events
// 2. if the request has a limit specified
// 3. if the number of events is equal to the request limit + 1 meaning there are
// additional events to request on a subsequent page.
if len(events) > 0 && req.Limit != 0 && len(events) == int(additionalEventsNumber) {
resp.NextPageToken = strconv.FormatInt(int64(page+1), 10)
events = events[:len(events)-1]
}
resp.Events = events
return resp, nil
}
func (m *mod) GetEvent(ctx context.Context, req *auditv1.GetEventRequest) (*auditv1.GetEventResponse, error) {
event, err := m.client.ReadEvent(ctx, req.EventId)
if err != nil {
return nil, err
}
resp := &auditv1.GetEventResponse{
Event: event,
}
return resp, nil
}