mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 14:59:53 +08:00
- Make vars const where possible - Move constants that are only used by the audit log into lib/events - Remove unused vars
691 lines
20 KiB
Go
691 lines
20 KiB
Go
/*
|
|
Copyright 2020 Gravitational, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package events
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/gravitational/trace"
|
|
"github.com/jonboulle/clockwork"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/gravitational/teleport"
|
|
apievents "github.com/gravitational/teleport/api/types/events"
|
|
"github.com/gravitational/teleport/lib/session"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
)
|
|
|
|
// AsyncBufferSize is a default buffer size for async emitters
|
|
const AsyncBufferSize = 1024
|
|
|
|
// AsyncEmitterConfig provides parameters for emitter
|
|
type AsyncEmitterConfig struct {
|
|
// Inner emits events to the underlying store
|
|
Inner apievents.Emitter
|
|
// BufferSize is a default buffer size for emitter
|
|
BufferSize int
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets default values
|
|
func (c *AsyncEmitterConfig) CheckAndSetDefaults() error {
|
|
if c.Inner == nil {
|
|
return trace.BadParameter("missing parameter Inner")
|
|
}
|
|
if c.BufferSize == 0 {
|
|
c.BufferSize = AsyncBufferSize
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewAsyncEmitter returns emitter that submits events
|
|
// without blocking the caller. It will start losing events
|
|
// on buffer overflow.
|
|
func NewAsyncEmitter(cfg AsyncEmitterConfig) (*AsyncEmitter, error) {
|
|
if err := cfg.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
a := &AsyncEmitter{
|
|
cancel: cancel,
|
|
ctx: ctx,
|
|
eventsCh: make(chan apievents.AuditEvent, cfg.BufferSize),
|
|
cfg: cfg,
|
|
}
|
|
go a.forward()
|
|
return a, nil
|
|
}
|
|
|
|
// AsyncEmitter accepts events to a buffered channel and emits
|
|
// events in a separate goroutine without blocking the caller.
|
|
type AsyncEmitter struct {
|
|
cfg AsyncEmitterConfig
|
|
eventsCh chan apievents.AuditEvent
|
|
cancel context.CancelFunc
|
|
ctx context.Context
|
|
}
|
|
|
|
// Close closes emitter and cancels all in flight events.
|
|
func (a *AsyncEmitter) Close() error {
|
|
a.cancel()
|
|
return nil
|
|
}
|
|
|
|
func (a *AsyncEmitter) forward() {
|
|
for {
|
|
select {
|
|
case <-a.ctx.Done():
|
|
return
|
|
case event := <-a.eventsCh:
|
|
err := a.cfg.Inner.EmitAuditEvent(a.ctx, event)
|
|
if err != nil {
|
|
log.WithError(err).Errorf("Failed to emit audit event.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// EmitAuditEvent emits audit event without blocking the caller. It will start
|
|
// losing events on buffer overflow, but it never fails.
|
|
func (a *AsyncEmitter) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
select {
|
|
case a.eventsCh <- event:
|
|
return nil
|
|
case <-ctx.Done():
|
|
return trace.ConnectionProblem(ctx.Err(), "context canceled or closed")
|
|
default:
|
|
log.Errorf("Failed to emit audit event %v(%v). This server's connection to the auth service appears to be slow.", event.GetType(), event.GetCode())
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// CheckingEmitterConfig provides parameters for emitter
|
|
type CheckingEmitterConfig struct {
|
|
// Inner emits events to the underlying store
|
|
Inner apievents.Emitter
|
|
// Clock is a clock interface, used in tests
|
|
Clock clockwork.Clock
|
|
// UIDGenerator is unique ID generator
|
|
UIDGenerator utils.UID
|
|
// ClusterName specifies the name of this teleport cluster
|
|
// as configured on the auth server
|
|
ClusterName string
|
|
}
|
|
|
|
// NewCheckingEmitter returns emitter that checks
|
|
// that all required fields are properly set
|
|
func NewCheckingEmitter(cfg CheckingEmitterConfig) (*CheckingEmitter, error) {
|
|
if err := cfg.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CheckingEmitter{
|
|
CheckingEmitterConfig: cfg,
|
|
}, nil
|
|
}
|
|
|
|
// CheckingEmitter ensures that event fields have been set properly
|
|
// and reports statistics for every wrapper
|
|
type CheckingEmitter struct {
|
|
CheckingEmitterConfig
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets default values
|
|
func (w *CheckingEmitterConfig) CheckAndSetDefaults() error {
|
|
if w.Inner == nil {
|
|
return trace.BadParameter("missing parameter Inner")
|
|
}
|
|
if w.Clock == nil {
|
|
w.Clock = clockwork.NewRealClock()
|
|
}
|
|
if w.UIDGenerator == nil {
|
|
w.UIDGenerator = utils.NewRealUID()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EmitAuditEvent emits audit event
|
|
func (r *CheckingEmitter) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
auditEmitEvent.Inc()
|
|
if err := checkAndSetEventFields(event, r.Clock, r.UIDGenerator, r.ClusterName); err != nil {
|
|
log.WithError(err).Errorf("Failed to emit audit event.")
|
|
AuditFailedEmit.Inc()
|
|
return trace.Wrap(err)
|
|
}
|
|
if err := r.Inner.EmitAuditEvent(ctx, event); err != nil {
|
|
AuditFailedEmit.Inc()
|
|
log.WithError(err).Errorf("Failed to emit audit event.")
|
|
return trace.Wrap(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkAndSetEventFields updates passed event fields with additional information
|
|
// common for all event types such as unique IDs, timestamps, codes, etc.
|
|
//
|
|
// This method is a "final stop" for various audit log implementations for
|
|
// updating event fields before it gets persisted in the backend.
|
|
func checkAndSetEventFields(event apievents.AuditEvent, clock clockwork.Clock, uid utils.UID, clusterName string) error {
|
|
if event.GetType() == "" {
|
|
return trace.BadParameter("missing mandatory event type field")
|
|
}
|
|
if event.GetCode() == "" && event.GetType() != SessionPrintEvent && event.GetType() != DesktopRecordingEvent {
|
|
return trace.BadParameter("missing mandatory event code field for %v event", event.GetType())
|
|
}
|
|
if event.GetID() == "" && event.GetType() != SessionPrintEvent && event.GetType() != DesktopRecordingEvent {
|
|
event.SetID(uid.New())
|
|
}
|
|
if event.GetTime().IsZero() {
|
|
event.SetTime(clock.Now().UTC().Round(time.Millisecond))
|
|
}
|
|
if event.GetClusterName() == "" {
|
|
event.SetClusterName(clusterName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewWriterEmitter returns a new instance of emitter writing to writer
|
|
func NewWriterEmitter(w io.WriteCloser) *WriterEmitter {
|
|
return &WriterEmitter{
|
|
w: w,
|
|
WriterLog: NewWriterLog(w),
|
|
}
|
|
}
|
|
|
|
// WriterEmitter is an emitter that emits all events
|
|
// to the external writer
|
|
type WriterEmitter struct {
|
|
w io.WriteCloser
|
|
*WriterLog
|
|
}
|
|
|
|
// Close closes the underlying io.WriteCloser passed in NewWriterEmitter
|
|
func (w *WriterEmitter) Close() error {
|
|
return trace.NewAggregate(
|
|
w.w.Close(),
|
|
w.WriterLog.Close())
|
|
}
|
|
|
|
// EmitAuditEvent writes the event to the writer
|
|
func (w *WriterEmitter) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
// line is the text to be logged
|
|
line, err := utils.FastMarshal(event)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
_, err = fmt.Fprintln(w.w, string(line))
|
|
return trace.ConvertSystemError(err)
|
|
}
|
|
|
|
// NewLoggingEmitter returns an emitter that logs all events to the console
|
|
// with the info level
|
|
func NewLoggingEmitter() *LoggingEmitter {
|
|
return &LoggingEmitter{}
|
|
}
|
|
|
|
// LoggingEmitter logs all events with info level
|
|
type LoggingEmitter struct {
|
|
}
|
|
|
|
// EmitAuditEvent logs audit event, skips session print events, session
|
|
// disk events and app session request events, because they are very verbose.
|
|
func (*LoggingEmitter) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
switch event.GetType() {
|
|
case ResizeEvent, SessionDiskEvent, SessionPrintEvent, AppSessionRequestEvent, "":
|
|
return nil
|
|
}
|
|
|
|
data, err := utils.FastMarshal(event)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
var fields log.Fields
|
|
err = utils.FastUnmarshal(data, &fields)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
fields[trace.Component] = teleport.Component(teleport.ComponentAuditLog)
|
|
|
|
log.WithFields(fields).Infof(event.GetType())
|
|
return nil
|
|
}
|
|
|
|
// NewMultiEmitter returns emitter that writes
|
|
// events to all emitters
|
|
func NewMultiEmitter(emitters ...apievents.Emitter) *MultiEmitter {
|
|
return &MultiEmitter{
|
|
emitters: emitters,
|
|
}
|
|
}
|
|
|
|
// MultiEmitter writes audit events to multiple emitters
|
|
type MultiEmitter struct {
|
|
emitters []apievents.Emitter
|
|
}
|
|
|
|
// EmitAuditEvent emits audit event to all emitters
|
|
func (m *MultiEmitter) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
var errors []error
|
|
for i := range m.emitters {
|
|
err := m.emitters[i].EmitAuditEvent(ctx, event)
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
}
|
|
return trace.NewAggregate(errors...)
|
|
}
|
|
|
|
// StreamerAndEmitter combines streamer and emitter to create stream emitter
|
|
type StreamerAndEmitter struct {
|
|
Streamer
|
|
apievents.Emitter
|
|
}
|
|
|
|
// CheckingStreamerConfig provides parameters for streamer
|
|
type CheckingStreamerConfig struct {
|
|
// Inner emits events to the underlying store
|
|
Inner Streamer
|
|
// Clock is a clock interface, used in tests
|
|
Clock clockwork.Clock
|
|
// UIDGenerator is unique ID generator
|
|
UIDGenerator utils.UID
|
|
// ClusterName specifies the name of this teleport cluster.
|
|
ClusterName string
|
|
}
|
|
|
|
// NewCheckingStream wraps stream and makes sure event UIDs and timing are in place
|
|
func NewCheckingStream(stream apievents.Stream, clock clockwork.Clock, clusterName string) apievents.Stream {
|
|
return &CheckingStream{
|
|
stream: stream,
|
|
clock: clock,
|
|
uidGenerator: utils.NewRealUID(),
|
|
clusterName: clusterName,
|
|
}
|
|
}
|
|
|
|
// NewCheckingStreamer returns streamer that checks
|
|
// that all required fields are properly set
|
|
func NewCheckingStreamer(cfg CheckingStreamerConfig) (*CheckingStreamer, error) {
|
|
if err := cfg.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CheckingStreamer{
|
|
CheckingStreamerConfig: cfg,
|
|
}, nil
|
|
}
|
|
|
|
// CheckingStreamer ensures that event fields have been set properly
|
|
// and reports statistics for every wrapper
|
|
type CheckingStreamer struct {
|
|
CheckingStreamerConfig
|
|
}
|
|
|
|
// CreateAuditStream creates audit event stream
|
|
func (s *CheckingStreamer) CreateAuditStream(ctx context.Context, sid session.ID) (apievents.Stream, error) {
|
|
stream, err := s.Inner.CreateAuditStream(ctx, sid)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CheckingStream{
|
|
clock: s.CheckingStreamerConfig.Clock,
|
|
uidGenerator: s.CheckingStreamerConfig.UIDGenerator,
|
|
clusterName: s.CheckingStreamerConfig.ClusterName,
|
|
stream: stream,
|
|
}, nil
|
|
}
|
|
|
|
// ResumeAuditStream resumes audit event stream
|
|
func (s *CheckingStreamer) ResumeAuditStream(ctx context.Context, sid session.ID, uploadID string) (apievents.Stream, error) {
|
|
stream, err := s.Inner.ResumeAuditStream(ctx, sid, uploadID)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CheckingStream{
|
|
clock: s.CheckingStreamerConfig.Clock,
|
|
uidGenerator: s.CheckingStreamerConfig.UIDGenerator,
|
|
clusterName: s.CheckingStreamerConfig.ClusterName,
|
|
stream: stream,
|
|
}, nil
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets default values
|
|
func (w *CheckingStreamerConfig) CheckAndSetDefaults() error {
|
|
if w.Inner == nil {
|
|
return trace.BadParameter("checking streamer: missing parameter Inner")
|
|
}
|
|
if w.ClusterName == "" {
|
|
return trace.BadParameter("checking streamer: missing parameter ClusterName")
|
|
}
|
|
if w.Clock == nil {
|
|
w.Clock = clockwork.NewRealClock()
|
|
}
|
|
if w.UIDGenerator == nil {
|
|
w.UIDGenerator = utils.NewRealUID()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckingStream verifies every event
|
|
type CheckingStream struct {
|
|
stream apievents.Stream
|
|
clock clockwork.Clock
|
|
uidGenerator utils.UID
|
|
clusterName string
|
|
}
|
|
|
|
// Close flushes non-uploaded flight stream data without marking
|
|
// the stream completed and closes the stream instance
|
|
func (s *CheckingStream) Close(ctx context.Context) error {
|
|
return s.stream.Close(ctx)
|
|
}
|
|
|
|
// Done returns channel closed when streamer is closed
|
|
// should be used to detect sending errors
|
|
func (s *CheckingStream) Done() <-chan struct{} {
|
|
return s.stream.Done()
|
|
}
|
|
|
|
// Status returns channel receiving updates about stream status
|
|
// last event index that was uploaded and upload ID
|
|
func (s *CheckingStream) Status() <-chan apievents.StreamStatus {
|
|
return s.stream.Status()
|
|
}
|
|
|
|
// Complete closes the stream and marks it finalized
|
|
func (s *CheckingStream) Complete(ctx context.Context) error {
|
|
return s.stream.Complete(ctx)
|
|
}
|
|
|
|
// EmitAuditEvent emits audit event
|
|
func (s *CheckingStream) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
if err := checkAndSetEventFields(event, s.clock, s.uidGenerator, s.clusterName); err != nil {
|
|
log.WithError(err).Errorf("Failed to emit audit event %v(%v).", event.GetType(), event.GetCode())
|
|
AuditFailedEmit.Inc()
|
|
return trace.Wrap(err)
|
|
}
|
|
if err := s.stream.EmitAuditEvent(ctx, event); err != nil {
|
|
AuditFailedEmit.Inc()
|
|
log.WithError(err).Errorf("Failed to emit audit event %v(%v).", event.GetType(), event.GetCode())
|
|
return trace.Wrap(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewTeeStreamer returns a streamer that forwards non print event
|
|
// to emitter in addition to sending them to the stream
|
|
func NewTeeStreamer(streamer Streamer, emitter apievents.Emitter) *TeeStreamer {
|
|
return &TeeStreamer{
|
|
Emitter: emitter,
|
|
streamer: streamer,
|
|
}
|
|
}
|
|
|
|
// CreateAuditStream creates audit event stream
|
|
func (t *TeeStreamer) CreateAuditStream(ctx context.Context, sid session.ID) (apievents.Stream, error) {
|
|
stream, err := t.streamer.CreateAuditStream(ctx, sid)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &TeeStream{stream: stream, emitter: t.Emitter}, nil
|
|
}
|
|
|
|
// ResumeAuditStream resumes audit event stream
|
|
func (t *TeeStreamer) ResumeAuditStream(ctx context.Context, sid session.ID, uploadID string) (apievents.Stream, error) {
|
|
stream, err := t.streamer.ResumeAuditStream(ctx, sid, uploadID)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &TeeStream{stream: stream, emitter: t.Emitter}, nil
|
|
}
|
|
|
|
// TeeStreamer creates streams that forwards non print events
|
|
// to emitter
|
|
type TeeStreamer struct {
|
|
apievents.Emitter
|
|
streamer Streamer
|
|
}
|
|
|
|
// TeeStream sends non print events to emitter
|
|
// in addition to the stream itself
|
|
type TeeStream struct {
|
|
emitter apievents.Emitter
|
|
stream apievents.Stream
|
|
}
|
|
|
|
// Done returns channel closed when streamer is closed
|
|
// should be used to detect sending errors
|
|
func (t *TeeStream) Done() <-chan struct{} {
|
|
return t.stream.Done()
|
|
}
|
|
|
|
// Status returns channel receiving updates about stream status
|
|
// last event index that was uploaded and upload ID
|
|
func (t *TeeStream) Status() <-chan apievents.StreamStatus {
|
|
return t.stream.Status()
|
|
}
|
|
|
|
// Close flushes non-uploaded flight stream data without marking
|
|
// the stream completed and closes the stream instance
|
|
func (t *TeeStream) Close(ctx context.Context) error {
|
|
return t.stream.Close(ctx)
|
|
}
|
|
|
|
// Complete closes the stream and marks it finalized
|
|
func (t *TeeStream) Complete(ctx context.Context) error {
|
|
return t.stream.Complete(ctx)
|
|
}
|
|
|
|
// EmitAuditEvent emits audit events and forwards session control events
|
|
// to the audit log
|
|
func (t *TeeStream) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
var errors []error
|
|
if err := t.stream.EmitAuditEvent(ctx, event); err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
// Forward session events except the ones that pollute global logs
|
|
switch event.GetType() {
|
|
case ResizeEvent, SessionDiskEvent, SessionPrintEvent, AppSessionRequestEvent, DesktopRecordingEvent, "":
|
|
return trace.NewAggregate(errors...)
|
|
}
|
|
if err := t.emitter.EmitAuditEvent(ctx, event); err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
return trace.NewAggregate(errors...)
|
|
}
|
|
|
|
// NewCallbackStreamer returns streamer that invokes callback on every
|
|
// action, is used in tests to inject failures
|
|
func NewCallbackStreamer(cfg CallbackStreamerConfig) (*CallbackStreamer, error) {
|
|
if err := cfg.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CallbackStreamer{
|
|
CallbackStreamerConfig: cfg,
|
|
}, nil
|
|
}
|
|
|
|
// CallbackStreamerConfig provides parameters for streamer
|
|
type CallbackStreamerConfig struct {
|
|
// Inner emits events to the underlying store
|
|
Inner Streamer
|
|
// OnCreateAuditStream is called on create audit stream
|
|
OnCreateAuditStream func(ctx context.Context, sid session.ID, inner Streamer) (apievents.Stream, error)
|
|
// OnResumeAuditStream is called on resuming audit stream
|
|
OnResumeAuditStream func(ctx context.Context, sid session.ID, uploadID string, inner Streamer) (apievents.Stream, error)
|
|
// OnEmitAuditEvent is called on emit audit event on a stream
|
|
OnEmitAuditEvent func(ctx context.Context, sid session.ID, event apievents.AuditEvent) error
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets default values
|
|
func (c *CallbackStreamerConfig) CheckAndSetDefaults() error {
|
|
if c.Inner == nil {
|
|
return trace.BadParameter("missing parameter Inner")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CallbackStreamer ensures that event fields have been set properly
|
|
// and reports statistics for every wrapper
|
|
type CallbackStreamer struct {
|
|
CallbackStreamerConfig
|
|
}
|
|
|
|
// CreateAuditStream creates audit event stream
|
|
func (s *CallbackStreamer) CreateAuditStream(ctx context.Context, sid session.ID) (apievents.Stream, error) {
|
|
var stream apievents.Stream
|
|
var err error
|
|
if s.OnCreateAuditStream != nil {
|
|
stream, err = s.OnCreateAuditStream(ctx, sid, s.Inner)
|
|
} else {
|
|
stream, err = s.Inner.CreateAuditStream(ctx, sid)
|
|
}
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CallbackStream{
|
|
stream: stream,
|
|
streamer: s,
|
|
}, nil
|
|
}
|
|
|
|
// ResumeAuditStream resumes audit event stream
|
|
func (s *CallbackStreamer) ResumeAuditStream(ctx context.Context, sid session.ID, uploadID string) (apievents.Stream, error) {
|
|
var stream apievents.Stream
|
|
var err error
|
|
if s.OnResumeAuditStream != nil {
|
|
stream, err = s.OnResumeAuditStream(ctx, sid, uploadID, s.Inner)
|
|
} else {
|
|
stream, err = s.Inner.ResumeAuditStream(ctx, sid, uploadID)
|
|
}
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &CallbackStream{
|
|
stream: stream,
|
|
sessionID: sid,
|
|
streamer: s,
|
|
}, nil
|
|
}
|
|
|
|
// CallbackStream call
|
|
type CallbackStream struct {
|
|
stream apievents.Stream
|
|
sessionID session.ID
|
|
streamer *CallbackStreamer
|
|
}
|
|
|
|
// Close flushes non-uploaded flight stream data without marking
|
|
// the stream completed and closes the stream instance
|
|
func (s *CallbackStream) Close(ctx context.Context) error {
|
|
return s.stream.Close(ctx)
|
|
}
|
|
|
|
// Done returns channel closed when streamer is closed
|
|
// should be used to detect sending errors
|
|
func (s *CallbackStream) Done() <-chan struct{} {
|
|
return s.stream.Done()
|
|
}
|
|
|
|
// Status returns channel receiving updates about stream status
|
|
// last event index that was uploaded and upload ID
|
|
func (s *CallbackStream) Status() <-chan apievents.StreamStatus {
|
|
return s.stream.Status()
|
|
}
|
|
|
|
// Complete closes the stream and marks it finalized
|
|
func (s *CallbackStream) Complete(ctx context.Context) error {
|
|
return s.stream.Complete(ctx)
|
|
}
|
|
|
|
// EmitAuditEvent emits audit event
|
|
func (s *CallbackStream) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent) error {
|
|
if s.streamer.OnEmitAuditEvent != nil {
|
|
if err := s.streamer.OnEmitAuditEvent(ctx, s.sessionID, event); err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
}
|
|
return s.stream.EmitAuditEvent(ctx, event)
|
|
}
|
|
|
|
// NewReportingStreamer reports upload events
|
|
// to the eventsC channel, if the channel is not nil.
|
|
func NewReportingStreamer(streamer Streamer, eventsC chan UploadEvent) *ReportingStreamer {
|
|
return &ReportingStreamer{
|
|
streamer: streamer,
|
|
eventsC: eventsC,
|
|
}
|
|
}
|
|
|
|
// ReportingStreamer reports upload events
|
|
// to the eventsC channel, if the channel is not nil.
|
|
type ReportingStreamer struct {
|
|
streamer Streamer
|
|
eventsC chan UploadEvent
|
|
}
|
|
|
|
// CreateAuditStream creates audit event stream
|
|
func (s *ReportingStreamer) CreateAuditStream(ctx context.Context, sid session.ID) (apievents.Stream, error) {
|
|
stream, err := s.streamer.CreateAuditStream(ctx, sid)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &ReportingStream{
|
|
Stream: stream,
|
|
eventsC: s.eventsC,
|
|
sessionID: sid,
|
|
}, nil
|
|
}
|
|
|
|
// ResumeAuditStream resumes audit event stream
|
|
func (s *ReportingStreamer) ResumeAuditStream(ctx context.Context, sid session.ID, uploadID string) (apievents.Stream, error) {
|
|
stream, err := s.streamer.ResumeAuditStream(ctx, sid, uploadID)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &ReportingStream{
|
|
Stream: stream,
|
|
sessionID: sid,
|
|
eventsC: s.eventsC,
|
|
}, nil
|
|
}
|
|
|
|
// ReportingStream reports status of uploads to the events channel
|
|
type ReportingStream struct {
|
|
apievents.Stream
|
|
sessionID session.ID
|
|
eventsC chan UploadEvent
|
|
}
|
|
|
|
// Complete closes the stream and marks it finalized
|
|
func (s *ReportingStream) Complete(ctx context.Context) error {
|
|
err := s.Stream.Complete(ctx)
|
|
if s.eventsC == nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
select {
|
|
case s.eventsC <- UploadEvent{
|
|
SessionID: string(s.sessionID),
|
|
Error: err,
|
|
}:
|
|
default:
|
|
log.Warningf("Skip send event on a blocked channel.")
|
|
}
|
|
return trace.Wrap(err)
|
|
}
|