k8s.io/apiserver@v0.31.1/pkg/audit/context_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package audit
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  	"testing"
    24  
    25  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  func TestEnabled(t *testing.T) {
    31  	tests := []struct {
    32  		name          string
    33  		ctx           *AuditContext
    34  		expectEnabled bool
    35  	}{{
    36  		name:          "nil context",
    37  		expectEnabled: false,
    38  	}, {
    39  		name:          "empty context",
    40  		ctx:           &AuditContext{},
    41  		expectEnabled: true, // An AuditContext should be considered enabled before the level is set
    42  	}, {
    43  		name:          "level None",
    44  		ctx:           &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelNone}},
    45  		expectEnabled: false,
    46  	}, {
    47  		name:          "level Metadata",
    48  		ctx:           &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelMetadata}},
    49  		expectEnabled: true,
    50  	}, {
    51  		name:          "level RequestResponse",
    52  		ctx:           &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelRequestResponse}},
    53  		expectEnabled: true,
    54  	}}
    55  
    56  	for _, test := range tests {
    57  		t.Run(test.name, func(t *testing.T) {
    58  			assert.Equal(t, test.expectEnabled, test.ctx.Enabled())
    59  		})
    60  	}
    61  }
    62  
    63  func TestAddAuditAnnotation(t *testing.T) {
    64  	const (
    65  		annotationKeyTemplate = "test-annotation-%d"
    66  		annotationValue       = "test-annotation-value"
    67  		annotationExtraValue  = "test-existing-annotation"
    68  		numAnnotations        = 10
    69  	)
    70  
    71  	expectAnnotations := func(t *testing.T, annotations map[string]string) {
    72  		assert.Len(t, annotations, numAnnotations)
    73  	}
    74  
    75  	ctxWithAnnotation := withAuditContextAndLevel(context.Background(), auditinternal.LevelMetadata)
    76  	AddAuditAnnotation(ctxWithAnnotation, fmt.Sprintf(annotationKeyTemplate, 0), annotationExtraValue)
    77  
    78  	tests := []struct {
    79  		description string
    80  		ctx         context.Context
    81  		validator   func(t *testing.T, ctx context.Context)
    82  	}{{
    83  		description: "no audit",
    84  		ctx:         context.Background(),
    85  		validator:   func(_ *testing.T, _ context.Context) {},
    86  	}, {
    87  		description: "context initialized, policy not evaluated",
    88  		// Audit context is initialized, but the policy has not yet been evaluated (no level).
    89  		// Annotations should be retained.
    90  		ctx: WithAuditContext(context.Background()),
    91  		validator: func(t *testing.T, ctx context.Context) {
    92  			ev := AuditContextFrom(ctx).Event
    93  			expectAnnotations(t, ev.Annotations)
    94  		},
    95  	}, {
    96  		description: "with metadata level",
    97  		ctx:         withAuditContextAndLevel(context.Background(), auditinternal.LevelMetadata),
    98  		validator: func(t *testing.T, ctx context.Context) {
    99  			ev := AuditContextFrom(ctx).Event
   100  			expectAnnotations(t, ev.Annotations)
   101  		},
   102  	}, {
   103  		description: "with none level",
   104  		ctx:         withAuditContextAndLevel(context.Background(), auditinternal.LevelNone),
   105  		validator: func(t *testing.T, ctx context.Context) {
   106  			ev := AuditContextFrom(ctx).Event
   107  			assert.Empty(t, ev.Annotations)
   108  		},
   109  	}, {
   110  		description: "with overlapping annotations",
   111  		ctx:         ctxWithAnnotation,
   112  		validator: func(t *testing.T, ctx context.Context) {
   113  			ev := AuditContextFrom(ctx).Event
   114  			expectAnnotations(t, ev.Annotations)
   115  			// Verify that the pre-existing annotation is not overwritten.
   116  			assert.Equal(t, annotationExtraValue, ev.Annotations[fmt.Sprintf(annotationKeyTemplate, 0)])
   117  		},
   118  	}}
   119  
   120  	for _, test := range tests {
   121  		t.Run(test.description, func(t *testing.T) {
   122  			var wg sync.WaitGroup
   123  			wg.Add(numAnnotations)
   124  			for i := 0; i < numAnnotations; i++ {
   125  				go func(i int) {
   126  					AddAuditAnnotation(test.ctx, fmt.Sprintf(annotationKeyTemplate, i), annotationValue)
   127  					wg.Done()
   128  				}(i)
   129  			}
   130  			wg.Wait()
   131  
   132  			test.validator(t, test.ctx)
   133  		})
   134  	}
   135  }
   136  
   137  func TestAuditAnnotationsWithAuditLoggingSetup(t *testing.T) {
   138  	// No audit context data in the request context
   139  	ctx := context.Background()
   140  	AddAuditAnnotation(ctx, "nil", "0")
   141  
   142  	// initialize audit context, policy not evaluated yet
   143  	ctx = WithAuditContext(ctx)
   144  	AddAuditAnnotation(ctx, "before-evaluation", "1")
   145  
   146  	// policy evaluated, audit logging enabled
   147  	if ac := AuditContextFrom(ctx); ac != nil {
   148  		ac.RequestAuditConfig.Level = auditinternal.LevelMetadata
   149  	}
   150  	AddAuditAnnotation(ctx, "after-evaluation", "2")
   151  
   152  	expected := map[string]string{
   153  		"before-evaluation": "1",
   154  		"after-evaluation":  "2",
   155  	}
   156  	actual := AuditContextFrom(ctx).Event.Annotations
   157  	assert.Equal(t, expected, actual)
   158  }
   159  
   160  func withAuditContextAndLevel(ctx context.Context, l auditinternal.Level) context.Context {
   161  	ctx = WithAuditContext(ctx)
   162  	ac := AuditContextFrom(ctx)
   163  	ac.RequestAuditConfig.Level = l
   164  	return ctx
   165  }