github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/client/events_test.go (about)

     1  // Copyright 2023 Gravitational, Inc
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package client
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/jonboulle/clockwork"
    21  	"github.com/stretchr/testify/require"
    22  	"google.golang.org/protobuf/proto"
    23  
    24  	authpb "github.com/gravitational/teleport/api/client/proto"
    25  	"github.com/gravitational/teleport/api/types"
    26  	"github.com/gravitational/teleport/api/types/accesslist"
    27  	accesslistv1conv "github.com/gravitational/teleport/api/types/accesslist/convert/v1"
    28  	"github.com/gravitational/teleport/api/types/header"
    29  )
    30  
    31  // TestEventEqual will test an event object against a google proto.Equal. This is
    32  // primarily to catch potential issues with using our "mixed" gogo + regular protobuf
    33  // strategy.
    34  func TestEventEqual(t *testing.T) {
    35  	clock := clockwork.NewFakeClock()
    36  	app1, err := types.NewAppV3(types.Metadata{
    37  		Name: "app1",
    38  	}, types.AppSpecV3{
    39  		URI:        "https://uri.com",
    40  		PublicAddr: "https://public-addr.com",
    41  	})
    42  	require.NoError(t, err)
    43  
    44  	app1Dupe, err := types.NewAppV3(types.Metadata{
    45  		Name: "app1",
    46  	}, types.AppSpecV3{
    47  		URI:        "https://uri.com",
    48  		PublicAddr: "https://public-addr.com",
    49  	})
    50  	require.NoError(t, err)
    51  
    52  	app2, err := types.NewAppV3(types.Metadata{
    53  		Name: "app2",
    54  	}, types.AppSpecV3{
    55  		URI:        "https://uri.com",
    56  		PublicAddr: "https://public-addr.com",
    57  	})
    58  	require.NoError(t, err)
    59  
    60  	accessList1 := newAccessList(t, "1", clock)
    61  	accessList2 := newAccessList(t, "2", clock)
    62  
    63  	tests := []struct {
    64  		name     string
    65  		event1   *authpb.Event
    66  		event2   *authpb.Event
    67  		expected bool
    68  	}{
    69  		{
    70  			name:     "empty equal",
    71  			event1:   &authpb.Event{},
    72  			event2:   &authpb.Event{},
    73  			expected: true,
    74  		},
    75  		{
    76  			name:   "empty not equal",
    77  			event1: &authpb.Event{},
    78  			event2: &authpb.Event{
    79  				Type: authpb.Operation_PUT,
    80  				Resource: &authpb.Event_App{
    81  					App: app1,
    82  				},
    83  			},
    84  			expected: false,
    85  		},
    86  		{
    87  			name: "gogo oneof equal",
    88  			event1: &authpb.Event{
    89  				Type: authpb.Operation_PUT,
    90  				Resource: &authpb.Event_App{
    91  					App: app1,
    92  				},
    93  			},
    94  			event2: &authpb.Event{
    95  				Type: authpb.Operation_PUT,
    96  				Resource: &authpb.Event_App{
    97  					App: app1Dupe,
    98  				},
    99  			},
   100  			expected: true,
   101  		},
   102  		{
   103  			name: "gogo oneof not equal",
   104  			event1: &authpb.Event{
   105  				Type: authpb.Operation_PUT,
   106  				Resource: &authpb.Event_App{
   107  					App: app1,
   108  				},
   109  			},
   110  			event2: &authpb.Event{
   111  				Type: authpb.Operation_PUT,
   112  				Resource: &authpb.Event_App{
   113  					App: app2,
   114  				},
   115  			},
   116  			expected: false,
   117  		},
   118  		{
   119  			name: "regular protobuf oneof equal",
   120  			event1: &authpb.Event{
   121  				Type: authpb.Operation_PUT,
   122  				Resource: &authpb.Event_AccessList{
   123  					AccessList: accesslistv1conv.ToProto(accessList1),
   124  				},
   125  			},
   126  			event2: &authpb.Event{
   127  				Type: authpb.Operation_PUT,
   128  				Resource: &authpb.Event_AccessList{
   129  					AccessList: accesslistv1conv.ToProto(accessList1),
   130  				},
   131  			},
   132  			expected: true,
   133  		},
   134  		{
   135  			name: "regular protobuf oneof not equal",
   136  			event1: &authpb.Event{
   137  				Type: authpb.Operation_PUT,
   138  				Resource: &authpb.Event_AccessList{
   139  					AccessList: accesslistv1conv.ToProto(accessList1),
   140  				},
   141  			},
   142  			event2: &authpb.Event{
   143  				Type: authpb.Operation_PUT,
   144  				Resource: &authpb.Event_AccessList{
   145  					AccessList: accesslistv1conv.ToProto(accessList2),
   146  				},
   147  			},
   148  			expected: false,
   149  		},
   150  	}
   151  
   152  	for _, test := range tests {
   153  		test := test
   154  		t.Run(test.name, func(t *testing.T) {
   155  			event1 := test.event1
   156  			event2 := test.event2
   157  			require.Equal(t, test.expected, proto.Equal(event1, event2))
   158  		})
   159  	}
   160  }
   161  
   162  func newAccessList(t *testing.T, name string, clock clockwork.Clock) *accesslist.AccessList {
   163  	t.Helper()
   164  
   165  	accessList, err := accesslist.NewAccessList(
   166  		header.Metadata{
   167  			Name: name,
   168  		},
   169  		accesslist.Spec{
   170  			Title:       "title",
   171  			Description: "test access list",
   172  			Owners: []accesslist.Owner{
   173  				{
   174  					Name:        "test-user1",
   175  					Description: "test user 1",
   176  				},
   177  				{
   178  					Name:        "test-user2",
   179  					Description: "test user 2",
   180  				},
   181  			},
   182  			Audit: accesslist.Audit{
   183  				NextAuditDate: clock.Now(),
   184  			},
   185  			MembershipRequires: accesslist.Requires{
   186  				Roles: []string{"mrole1", "mrole2"},
   187  				Traits: map[string][]string{
   188  					"mtrait1": {"mvalue1", "mvalue2"},
   189  					"mtrait2": {"mvalue3", "mvalue4"},
   190  				},
   191  			},
   192  			OwnershipRequires: accesslist.Requires{
   193  				Roles: []string{"orole1", "orole2"},
   194  				Traits: map[string][]string{
   195  					"otrait1": {"ovalue1", "ovalue2"},
   196  					"otrait2": {"ovalue3", "ovalue4"},
   197  				},
   198  			},
   199  			Grants: accesslist.Grants{
   200  				Roles: []string{"grole1", "grole2"},
   201  				Traits: map[string][]string{
   202  					"gtrait1": {"gvalue1", "gvalue2"},
   203  					"gtrait2": {"gvalue3", "gvalue4"},
   204  				},
   205  			},
   206  		},
   207  	)
   208  	require.NoError(t, err)
   209  
   210  	return accessList
   211  }