feat: add more filters to audit logs

Add multiple new filters to audit logs. Through the UI, there will be a generic search box and the ability to sort columns. Through the CLI, there will be support for the same plus also direct filters for event_type, resource_type, resource_id, cluster_id, and actor.

Signed-off-by: Edward Sammut Alessi <edward.sammutalessi@siderolabs.com>
This commit is contained in:
Edward Sammut Alessi 2026-04-14 16:39:25 +02:00
parent 590ea2e370
commit 488b020b2e
No known key found for this signature in database
GPG Key ID: 65558E016966977A
21 changed files with 1467 additions and 193 deletions

View File

@ -78,6 +78,180 @@ func (SchematicBootloader) EnumDescriptor() ([]byte, []int) {
return file_omni_management_management_proto_rawDescGZIP(), []int{0}
}
type AuditLogEventType int32
const (
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UNSPECIFIED AuditLogEventType = 0
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_CREATE AuditLogEventType = 1
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE AuditLogEventType = 2
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS AuditLogEventType = 3
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_DESTROY AuditLogEventType = 4
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TEARDOWN AuditLogEventType = 5
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS AuditLogEventType = 6
AuditLogEventType_AUDIT_LOG_EVENT_TYPE_K8S_ACCESS AuditLogEventType = 7
)
// Enum value maps for AuditLogEventType.
var (
AuditLogEventType_name = map[int32]string{
0: "AUDIT_LOG_EVENT_TYPE_UNSPECIFIED",
1: "AUDIT_LOG_EVENT_TYPE_CREATE",
2: "AUDIT_LOG_EVENT_TYPE_UPDATE",
3: "AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS",
4: "AUDIT_LOG_EVENT_TYPE_DESTROY",
5: "AUDIT_LOG_EVENT_TYPE_TEARDOWN",
6: "AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS",
7: "AUDIT_LOG_EVENT_TYPE_K8S_ACCESS",
}
AuditLogEventType_value = map[string]int32{
"AUDIT_LOG_EVENT_TYPE_UNSPECIFIED": 0,
"AUDIT_LOG_EVENT_TYPE_CREATE": 1,
"AUDIT_LOG_EVENT_TYPE_UPDATE": 2,
"AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS": 3,
"AUDIT_LOG_EVENT_TYPE_DESTROY": 4,
"AUDIT_LOG_EVENT_TYPE_TEARDOWN": 5,
"AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS": 6,
"AUDIT_LOG_EVENT_TYPE_K8S_ACCESS": 7,
}
)
func (x AuditLogEventType) Enum() *AuditLogEventType {
p := new(AuditLogEventType)
*p = x
return p
}
func (x AuditLogEventType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AuditLogEventType) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[1].Descriptor()
}
func (AuditLogEventType) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[1]
}
func (x AuditLogEventType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AuditLogEventType.Descriptor instead.
func (AuditLogEventType) EnumDescriptor() ([]byte, []int) {
return file_omni_management_management_proto_rawDescGZIP(), []int{1}
}
type AuditLogOrderByField int32
const (
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED AuditLogOrderByField = 0
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_DATE AuditLogOrderByField = 1
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE AuditLogOrderByField = 2
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE AuditLogOrderByField = 3
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID AuditLogOrderByField = 4
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID AuditLogOrderByField = 5
AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_ACTOR AuditLogOrderByField = 6
)
// Enum value maps for AuditLogOrderByField.
var (
AuditLogOrderByField_name = map[int32]string{
0: "AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED",
1: "AUDIT_LOG_ORDER_BY_FIELD_DATE",
2: "AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE",
3: "AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE",
4: "AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID",
5: "AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID",
6: "AUDIT_LOG_ORDER_BY_FIELD_ACTOR",
}
AuditLogOrderByField_value = map[string]int32{
"AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED": 0,
"AUDIT_LOG_ORDER_BY_FIELD_DATE": 1,
"AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE": 2,
"AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE": 3,
"AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID": 4,
"AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID": 5,
"AUDIT_LOG_ORDER_BY_FIELD_ACTOR": 6,
}
)
func (x AuditLogOrderByField) Enum() *AuditLogOrderByField {
p := new(AuditLogOrderByField)
*p = x
return p
}
func (x AuditLogOrderByField) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AuditLogOrderByField) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[2].Descriptor()
}
func (AuditLogOrderByField) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[2]
}
func (x AuditLogOrderByField) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AuditLogOrderByField.Descriptor instead.
func (AuditLogOrderByField) EnumDescriptor() ([]byte, []int) {
return file_omni_management_management_proto_rawDescGZIP(), []int{2}
}
type AuditLogOrderByDir int32
const (
AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED AuditLogOrderByDir = 0
AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_ASC AuditLogOrderByDir = 1
AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_DESC AuditLogOrderByDir = 2
)
// Enum value maps for AuditLogOrderByDir.
var (
AuditLogOrderByDir_name = map[int32]string{
0: "AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED",
1: "AUDIT_LOG_ORDER_BY_DIR_ASC",
2: "AUDIT_LOG_ORDER_BY_DIR_DESC",
}
AuditLogOrderByDir_value = map[string]int32{
"AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED": 0,
"AUDIT_LOG_ORDER_BY_DIR_ASC": 1,
"AUDIT_LOG_ORDER_BY_DIR_DESC": 2,
}
)
func (x AuditLogOrderByDir) Enum() *AuditLogOrderByDir {
p := new(AuditLogOrderByDir)
*p = x
return p
}
func (x AuditLogOrderByDir) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AuditLogOrderByDir) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[3].Descriptor()
}
func (AuditLogOrderByDir) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[3]
}
func (x AuditLogOrderByDir) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AuditLogOrderByDir.Descriptor instead.
func (AuditLogOrderByDir) EnumDescriptor() ([]byte, []int) {
return file_omni_management_management_proto_rawDescGZIP(), []int{3}
}
type KubernetesSSAOptions_InventoryPolicy int32
const (
@ -111,11 +285,11 @@ func (x KubernetesSSAOptions_InventoryPolicy) String() string {
}
func (KubernetesSSAOptions_InventoryPolicy) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[1].Descriptor()
return file_omni_management_management_proto_enumTypes[4].Descriptor()
}
func (KubernetesSSAOptions_InventoryPolicy) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[1]
return &file_omni_management_management_proto_enumTypes[4]
}
func (x KubernetesSSAOptions_InventoryPolicy) Number() protoreflect.EnumNumber {
@ -160,11 +334,11 @@ func (x KubernetesSyncManifestResponse_ResponseType) String() string {
}
func (KubernetesSyncManifestResponse_ResponseType) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[2].Descriptor()
return file_omni_management_management_proto_enumTypes[5].Descriptor()
}
func (KubernetesSyncManifestResponse_ResponseType) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[2]
return &file_omni_management_management_proto_enumTypes[5]
}
func (x KubernetesSyncManifestResponse_ResponseType) Number() protoreflect.EnumNumber {
@ -209,11 +383,11 @@ func (x CreateSchematicRequest_SiderolinkGRPCTunnelMode) String() string {
}
func (CreateSchematicRequest_SiderolinkGRPCTunnelMode) Descriptor() protoreflect.EnumDescriptor {
return file_omni_management_management_proto_enumTypes[3].Descriptor()
return file_omni_management_management_proto_enumTypes[6].Descriptor()
}
func (CreateSchematicRequest_SiderolinkGRPCTunnelMode) Type() protoreflect.EnumType {
return &file_omni_management_management_proto_enumTypes[3]
return &file_omni_management_management_proto_enumTypes[6]
}
func (x CreateSchematicRequest_SiderolinkGRPCTunnelMode) Number() protoreflect.EnumNumber {
@ -1497,6 +1671,14 @@ type ReadAuditLogRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
StartTime string `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
EndTime string `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"`
OrderByField AuditLogOrderByField `protobuf:"varint,3,opt,name=order_by_field,json=orderByField,proto3,enum=management.AuditLogOrderByField" json:"order_by_field,omitempty"`
OrderByDir AuditLogOrderByDir `protobuf:"varint,4,opt,name=order_by_dir,json=orderByDir,proto3,enum=management.AuditLogOrderByDir" json:"order_by_dir,omitempty"`
Search string `protobuf:"bytes,5,opt,name=search,proto3" json:"search,omitempty"`
EventType AuditLogEventType `protobuf:"varint,6,opt,name=event_type,json=eventType,proto3,enum=management.AuditLogEventType" json:"event_type,omitempty"`
ResourceType string `protobuf:"bytes,7,opt,name=resource_type,json=resourceType,proto3" json:"resource_type,omitempty"`
ResourceId string `protobuf:"bytes,8,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
ClusterId string `protobuf:"bytes,9,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
Actor string `protobuf:"bytes,10,opt,name=actor,proto3" json:"actor,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -1545,6 +1727,62 @@ func (x *ReadAuditLogRequest) GetEndTime() string {
return ""
}
func (x *ReadAuditLogRequest) GetOrderByField() AuditLogOrderByField {
if x != nil {
return x.OrderByField
}
return AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED
}
func (x *ReadAuditLogRequest) GetOrderByDir() AuditLogOrderByDir {
if x != nil {
return x.OrderByDir
}
return AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED
}
func (x *ReadAuditLogRequest) GetSearch() string {
if x != nil {
return x.Search
}
return ""
}
func (x *ReadAuditLogRequest) GetEventType() AuditLogEventType {
if x != nil {
return x.EventType
}
return AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UNSPECIFIED
}
func (x *ReadAuditLogRequest) GetResourceType() string {
if x != nil {
return x.ResourceType
}
return ""
}
func (x *ReadAuditLogRequest) GetResourceId() string {
if x != nil {
return x.ResourceId
}
return ""
}
func (x *ReadAuditLogRequest) GetClusterId() string {
if x != nil {
return x.ClusterId
}
return ""
}
func (x *ReadAuditLogRequest) GetActor() string {
if x != nil {
return x.Actor
}
return ""
}
type ReadAuditLogResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
AuditLog []byte `protobuf:"bytes,1,opt,name=audit_log,json=auditLog,proto3" json:"audit_log,omitempty"`
@ -2901,11 +3139,24 @@ const file_omni_management_management_proto_rawDesc = "" +
"\x05error\x18\x02 \x01(\tR\x05error\x12\x14\n" +
"\x05state\x18\x03 \x01(\tR\x05state\x12\x14\n" +
"\x05total\x18\x04 \x01(\x05R\x05total\x12\x14\n" +
"\x05value\x18\x05 \x01(\x05R\x05value\"O\n" +
"\x05value\x18\x05 \x01(\x05R\x05value\"\xaa\x03\n" +
"\x13ReadAuditLogRequest\x12\x1d\n" +
"\n" +
"start_time\x18\x01 \x01(\tR\tstartTime\x12\x19\n" +
"\bend_time\x18\x02 \x01(\tR\aendTime\"3\n" +
"\bend_time\x18\x02 \x01(\tR\aendTime\x12F\n" +
"\x0eorder_by_field\x18\x03 \x01(\x0e2 .management.AuditLogOrderByFieldR\forderByField\x12@\n" +
"\forder_by_dir\x18\x04 \x01(\x0e2\x1e.management.AuditLogOrderByDirR\n" +
"orderByDir\x12\x16\n" +
"\x06search\x18\x05 \x01(\tR\x06search\x12<\n" +
"\n" +
"event_type\x18\x06 \x01(\x0e2\x1d.management.AuditLogEventTypeR\teventType\x12#\n" +
"\rresource_type\x18\a \x01(\tR\fresourceType\x12\x1f\n" +
"\vresource_id\x18\b \x01(\tR\n" +
"resourceId\x12\x1d\n" +
"\n" +
"cluster_id\x18\t \x01(\tR\tclusterId\x12\x14\n" +
"\x05actor\x18\n" +
" \x01(\tR\x05actor\"3\n" +
"\x14ReadAuditLogResponse\x12\x1b\n" +
"\taudit_log\x18\x01 \x01(\fR\bauditLog\"G\n" +
"\x19ValidateJsonSchemaRequest\x12\x12\n" +
@ -2969,7 +3220,28 @@ const file_omni_management_management_proto_rawDesc = "" +
"\tBOOT_AUTO\x10\x00\x12\r\n" +
"\tBOOT_DUAL\x10\x01\x12\v\n" +
"\aBOOT_SD\x10\x02\x12\r\n" +
"\tBOOT_GRUB\x10\x032\xa4\x10\n" +
"\tBOOT_GRUB\x10\x03*\xbc\x02\n" +
"\x11AuditLogEventType\x12$\n" +
" AUDIT_LOG_EVENT_TYPE_UNSPECIFIED\x10\x00\x12\x1f\n" +
"\x1bAUDIT_LOG_EVENT_TYPE_CREATE\x10\x01\x12\x1f\n" +
"\x1bAUDIT_LOG_EVENT_TYPE_UPDATE\x10\x02\x12.\n" +
"*AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS\x10\x03\x12 \n" +
"\x1cAUDIT_LOG_EVENT_TYPE_DESTROY\x10\x04\x12!\n" +
"\x1dAUDIT_LOG_EVENT_TYPE_TEARDOWN\x10\x05\x12%\n" +
"!AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS\x10\x06\x12#\n" +
"\x1fAUDIT_LOG_EVENT_TYPE_K8S_ACCESS\x10\a*\xaf\x02\n" +
"\x14AuditLogOrderByField\x12(\n" +
"$AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED\x10\x00\x12!\n" +
"\x1dAUDIT_LOG_ORDER_BY_FIELD_DATE\x10\x01\x12'\n" +
"#AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE\x10\x02\x12*\n" +
"&AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE\x10\x03\x12(\n" +
"$AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID\x10\x04\x12'\n" +
"#AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID\x10\x05\x12\"\n" +
"\x1eAUDIT_LOG_ORDER_BY_FIELD_ACTOR\x10\x06*}\n" +
"\x12AuditLogOrderByDir\x12&\n" +
"\"AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED\x10\x00\x12\x1e\n" +
"\x1aAUDIT_LOG_ORDER_BY_DIR_ASC\x10\x01\x12\x1f\n" +
"\x1bAUDIT_LOG_ORDER_BY_DIR_DESC\x10\x022\xa4\x10\n" +
"\x11ManagementService\x12K\n" +
"\n" +
"Kubeconfig\x12\x1d.management.KubeconfigRequest\x1a\x1e.management.KubeconfigResponse\x12N\n" +
@ -3011,138 +3283,144 @@ func file_omni_management_management_proto_rawDescGZIP() []byte {
return file_omni_management_management_proto_rawDescData
}
var file_omni_management_management_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_omni_management_management_proto_enumTypes = make([]protoimpl.EnumInfo, 7)
var file_omni_management_management_proto_msgTypes = make([]protoimpl.MessageInfo, 48)
var file_omni_management_management_proto_goTypes = []any{
(SchematicBootloader)(0), // 0: management.SchematicBootloader
(KubernetesSSAOptions_InventoryPolicy)(0), // 1: management.KubernetesSSAOptions.InventoryPolicy
(KubernetesSyncManifestResponse_ResponseType)(0), // 2: management.KubernetesSyncManifestResponse.ResponseType
(CreateSchematicRequest_SiderolinkGRPCTunnelMode)(0), // 3: management.CreateSchematicRequest.SiderolinkGRPCTunnelMode
(*KubeconfigResponse)(nil), // 4: management.KubeconfigResponse
(*TalosconfigResponse)(nil), // 5: management.TalosconfigResponse
(*OmniconfigResponse)(nil), // 6: management.OmniconfigResponse
(*MachineLogsRequest)(nil), // 7: management.MachineLogsRequest
(*ValidateConfigRequest)(nil), // 8: management.ValidateConfigRequest
(*TalosconfigRequest)(nil), // 9: management.TalosconfigRequest
(*CreateServiceAccountRequest)(nil), // 10: management.CreateServiceAccountRequest
(*CreateServiceAccountResponse)(nil), // 11: management.CreateServiceAccountResponse
(*RenewServiceAccountRequest)(nil), // 12: management.RenewServiceAccountRequest
(*RenewServiceAccountResponse)(nil), // 13: management.RenewServiceAccountResponse
(*DestroyServiceAccountRequest)(nil), // 14: management.DestroyServiceAccountRequest
(*ListServiceAccountsResponse)(nil), // 15: management.ListServiceAccountsResponse
(*KubeconfigRequest)(nil), // 16: management.KubeconfigRequest
(*KubernetesUpgradePreChecksRequest)(nil), // 17: management.KubernetesUpgradePreChecksRequest
(*KubernetesUpgradePreChecksResponse)(nil), // 18: management.KubernetesUpgradePreChecksResponse
(*KubernetesSSAOptions)(nil), // 19: management.KubernetesSSAOptions
(*KubernetesSyncManifestRequest)(nil), // 20: management.KubernetesSyncManifestRequest
(*KubernetesSyncManifestResponse)(nil), // 21: management.KubernetesSyncManifestResponse
(*CreateSchematicRequest)(nil), // 22: management.CreateSchematicRequest
(*CreateSchematicResponse)(nil), // 23: management.CreateSchematicResponse
(*GetSupportBundleRequest)(nil), // 24: management.GetSupportBundleRequest
(*GetSupportBundleResponse)(nil), // 25: management.GetSupportBundleResponse
(*ReadAuditLogRequest)(nil), // 26: management.ReadAuditLogRequest
(*ReadAuditLogResponse)(nil), // 27: management.ReadAuditLogResponse
(*ValidateJsonSchemaRequest)(nil), // 28: management.ValidateJsonSchemaRequest
(*ValidateJsonSchemaResponse)(nil), // 29: management.ValidateJsonSchemaResponse
(*MaintenanceUpgradeRequest)(nil), // 30: management.MaintenanceUpgradeRequest
(*MaintenanceUpgradeResponse)(nil), // 31: management.MaintenanceUpgradeResponse
(*GetMachineJoinConfigRequest)(nil), // 32: management.GetMachineJoinConfigRequest
(*GetMachineJoinConfigResponse)(nil), // 33: management.GetMachineJoinConfigResponse
(*GenJoinTokenResponse)(nil), // 34: management.GenJoinTokenResponse
(*CreateJoinTokenRequest)(nil), // 35: management.CreateJoinTokenRequest
(*CreateJoinTokenResponse)(nil), // 36: management.CreateJoinTokenResponse
(*ResetNodeUniqueTokenRequest)(nil), // 37: management.ResetNodeUniqueTokenRequest
(*ResetNodeUniqueTokenResponse)(nil), // 38: management.ResetNodeUniqueTokenResponse
(*CreateUserRequest)(nil), // 39: management.CreateUserRequest
(*CreateUserResponse)(nil), // 40: management.CreateUserResponse
(*UpdateUserRequest)(nil), // 41: management.UpdateUserRequest
(*DestroyUserRequest)(nil), // 42: management.DestroyUserRequest
(*ListUsersResponse)(nil), // 43: management.ListUsersResponse
(*ListServiceAccountsResponse_ServiceAccount)(nil), // 44: management.ListServiceAccountsResponse.ServiceAccount
(*ListServiceAccountsResponse_ServiceAccount_PgpPublicKey)(nil), // 45: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey
(*CreateSchematicRequest_Overlay)(nil), // 46: management.CreateSchematicRequest.Overlay
nil, // 47: management.CreateSchematicRequest.MetaValuesEntry
(*GetSupportBundleResponse_Progress)(nil), // 48: management.GetSupportBundleResponse.Progress
(*ValidateJsonSchemaResponse_Error)(nil), // 49: management.ValidateJsonSchemaResponse.Error
(*ListUsersResponse_User)(nil), // 50: management.ListUsersResponse.User
nil, // 51: management.ListUsersResponse.User.SamlLabelsEntry
(*durationpb.Duration)(nil), // 52: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 53: google.protobuf.Timestamp
(*emptypb.Empty)(nil), // 54: google.protobuf.Empty
(*common.Data)(nil), // 55: common.Data
(AuditLogEventType)(0), // 1: management.AuditLogEventType
(AuditLogOrderByField)(0), // 2: management.AuditLogOrderByField
(AuditLogOrderByDir)(0), // 3: management.AuditLogOrderByDir
(KubernetesSSAOptions_InventoryPolicy)(0), // 4: management.KubernetesSSAOptions.InventoryPolicy
(KubernetesSyncManifestResponse_ResponseType)(0), // 5: management.KubernetesSyncManifestResponse.ResponseType
(CreateSchematicRequest_SiderolinkGRPCTunnelMode)(0), // 6: management.CreateSchematicRequest.SiderolinkGRPCTunnelMode
(*KubeconfigResponse)(nil), // 7: management.KubeconfigResponse
(*TalosconfigResponse)(nil), // 8: management.TalosconfigResponse
(*OmniconfigResponse)(nil), // 9: management.OmniconfigResponse
(*MachineLogsRequest)(nil), // 10: management.MachineLogsRequest
(*ValidateConfigRequest)(nil), // 11: management.ValidateConfigRequest
(*TalosconfigRequest)(nil), // 12: management.TalosconfigRequest
(*CreateServiceAccountRequest)(nil), // 13: management.CreateServiceAccountRequest
(*CreateServiceAccountResponse)(nil), // 14: management.CreateServiceAccountResponse
(*RenewServiceAccountRequest)(nil), // 15: management.RenewServiceAccountRequest
(*RenewServiceAccountResponse)(nil), // 16: management.RenewServiceAccountResponse
(*DestroyServiceAccountRequest)(nil), // 17: management.DestroyServiceAccountRequest
(*ListServiceAccountsResponse)(nil), // 18: management.ListServiceAccountsResponse
(*KubeconfigRequest)(nil), // 19: management.KubeconfigRequest
(*KubernetesUpgradePreChecksRequest)(nil), // 20: management.KubernetesUpgradePreChecksRequest
(*KubernetesUpgradePreChecksResponse)(nil), // 21: management.KubernetesUpgradePreChecksResponse
(*KubernetesSSAOptions)(nil), // 22: management.KubernetesSSAOptions
(*KubernetesSyncManifestRequest)(nil), // 23: management.KubernetesSyncManifestRequest
(*KubernetesSyncManifestResponse)(nil), // 24: management.KubernetesSyncManifestResponse
(*CreateSchematicRequest)(nil), // 25: management.CreateSchematicRequest
(*CreateSchematicResponse)(nil), // 26: management.CreateSchematicResponse
(*GetSupportBundleRequest)(nil), // 27: management.GetSupportBundleRequest
(*GetSupportBundleResponse)(nil), // 28: management.GetSupportBundleResponse
(*ReadAuditLogRequest)(nil), // 29: management.ReadAuditLogRequest
(*ReadAuditLogResponse)(nil), // 30: management.ReadAuditLogResponse
(*ValidateJsonSchemaRequest)(nil), // 31: management.ValidateJsonSchemaRequest
(*ValidateJsonSchemaResponse)(nil), // 32: management.ValidateJsonSchemaResponse
(*MaintenanceUpgradeRequest)(nil), // 33: management.MaintenanceUpgradeRequest
(*MaintenanceUpgradeResponse)(nil), // 34: management.MaintenanceUpgradeResponse
(*GetMachineJoinConfigRequest)(nil), // 35: management.GetMachineJoinConfigRequest
(*GetMachineJoinConfigResponse)(nil), // 36: management.GetMachineJoinConfigResponse
(*GenJoinTokenResponse)(nil), // 37: management.GenJoinTokenResponse
(*CreateJoinTokenRequest)(nil), // 38: management.CreateJoinTokenRequest
(*CreateJoinTokenResponse)(nil), // 39: management.CreateJoinTokenResponse
(*ResetNodeUniqueTokenRequest)(nil), // 40: management.ResetNodeUniqueTokenRequest
(*ResetNodeUniqueTokenResponse)(nil), // 41: management.ResetNodeUniqueTokenResponse
(*CreateUserRequest)(nil), // 42: management.CreateUserRequest
(*CreateUserResponse)(nil), // 43: management.CreateUserResponse
(*UpdateUserRequest)(nil), // 44: management.UpdateUserRequest
(*DestroyUserRequest)(nil), // 45: management.DestroyUserRequest
(*ListUsersResponse)(nil), // 46: management.ListUsersResponse
(*ListServiceAccountsResponse_ServiceAccount)(nil), // 47: management.ListServiceAccountsResponse.ServiceAccount
(*ListServiceAccountsResponse_ServiceAccount_PgpPublicKey)(nil), // 48: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey
(*CreateSchematicRequest_Overlay)(nil), // 49: management.CreateSchematicRequest.Overlay
nil, // 50: management.CreateSchematicRequest.MetaValuesEntry
(*GetSupportBundleResponse_Progress)(nil), // 51: management.GetSupportBundleResponse.Progress
(*ValidateJsonSchemaResponse_Error)(nil), // 52: management.ValidateJsonSchemaResponse.Error
(*ListUsersResponse_User)(nil), // 53: management.ListUsersResponse.User
nil, // 54: management.ListUsersResponse.User.SamlLabelsEntry
(*durationpb.Duration)(nil), // 55: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 56: google.protobuf.Timestamp
(*emptypb.Empty)(nil), // 57: google.protobuf.Empty
(*common.Data)(nil), // 58: common.Data
}
var file_omni_management_management_proto_depIdxs = []int32{
44, // 0: management.ListServiceAccountsResponse.service_accounts:type_name -> management.ListServiceAccountsResponse.ServiceAccount
52, // 1: management.KubeconfigRequest.service_account_ttl:type_name -> google.protobuf.Duration
1, // 2: management.KubernetesSSAOptions.inventory_policy:type_name -> management.KubernetesSSAOptions.InventoryPolicy
52, // 3: management.KubernetesSSAOptions.reconcile_timeout:type_name -> google.protobuf.Duration
19, // 4: management.KubernetesSyncManifestRequest.ssa:type_name -> management.KubernetesSSAOptions
2, // 5: management.KubernetesSyncManifestResponse.response_type:type_name -> management.KubernetesSyncManifestResponse.ResponseType
47, // 6: management.CreateSchematicRequest.meta_values:type_name -> management.CreateSchematicRequest.MetaValuesEntry
3, // 7: management.CreateSchematicRequest.siderolink_grpc_tunnel_mode:type_name -> management.CreateSchematicRequest.SiderolinkGRPCTunnelMode
46, // 8: management.CreateSchematicRequest.overlay:type_name -> management.CreateSchematicRequest.Overlay
47, // 0: management.ListServiceAccountsResponse.service_accounts:type_name -> management.ListServiceAccountsResponse.ServiceAccount
55, // 1: management.KubeconfigRequest.service_account_ttl:type_name -> google.protobuf.Duration
4, // 2: management.KubernetesSSAOptions.inventory_policy:type_name -> management.KubernetesSSAOptions.InventoryPolicy
55, // 3: management.KubernetesSSAOptions.reconcile_timeout:type_name -> google.protobuf.Duration
22, // 4: management.KubernetesSyncManifestRequest.ssa:type_name -> management.KubernetesSSAOptions
5, // 5: management.KubernetesSyncManifestResponse.response_type:type_name -> management.KubernetesSyncManifestResponse.ResponseType
50, // 6: management.CreateSchematicRequest.meta_values:type_name -> management.CreateSchematicRequest.MetaValuesEntry
6, // 7: management.CreateSchematicRequest.siderolink_grpc_tunnel_mode:type_name -> management.CreateSchematicRequest.SiderolinkGRPCTunnelMode
49, // 8: management.CreateSchematicRequest.overlay:type_name -> management.CreateSchematicRequest.Overlay
0, // 9: management.CreateSchematicRequest.bootloader:type_name -> management.SchematicBootloader
48, // 10: management.GetSupportBundleResponse.progress:type_name -> management.GetSupportBundleResponse.Progress
49, // 11: management.ValidateJsonSchemaResponse.errors:type_name -> management.ValidateJsonSchemaResponse.Error
53, // 12: management.CreateJoinTokenRequest.expiration_time:type_name -> google.protobuf.Timestamp
50, // 13: management.ListUsersResponse.users:type_name -> management.ListUsersResponse.User
45, // 14: management.ListServiceAccountsResponse.ServiceAccount.pgp_public_keys:type_name -> management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey
53, // 15: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.expiration:type_name -> google.protobuf.Timestamp
53, // 16: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.created:type_name -> google.protobuf.Timestamp
53, // 17: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.last_used:type_name -> google.protobuf.Timestamp
49, // 18: management.ValidateJsonSchemaResponse.Error.errors:type_name -> management.ValidateJsonSchemaResponse.Error
51, // 19: management.ListUsersResponse.User.saml_labels:type_name -> management.ListUsersResponse.User.SamlLabelsEntry
16, // 20: management.ManagementService.Kubeconfig:input_type -> management.KubeconfigRequest
9, // 21: management.ManagementService.Talosconfig:input_type -> management.TalosconfigRequest
54, // 22: management.ManagementService.Omniconfig:input_type -> google.protobuf.Empty
7, // 23: management.ManagementService.MachineLogs:input_type -> management.MachineLogsRequest
8, // 24: management.ManagementService.ValidateConfig:input_type -> management.ValidateConfigRequest
28, // 25: management.ManagementService.ValidateJSONSchema:input_type -> management.ValidateJsonSchemaRequest
10, // 26: management.ManagementService.CreateServiceAccount:input_type -> management.CreateServiceAccountRequest
12, // 27: management.ManagementService.RenewServiceAccount:input_type -> management.RenewServiceAccountRequest
54, // 28: management.ManagementService.ListServiceAccounts:input_type -> google.protobuf.Empty
14, // 29: management.ManagementService.DestroyServiceAccount:input_type -> management.DestroyServiceAccountRequest
17, // 30: management.ManagementService.KubernetesUpgradePreChecks:input_type -> management.KubernetesUpgradePreChecksRequest
20, // 31: management.ManagementService.KubernetesSyncManifests:input_type -> management.KubernetesSyncManifestRequest
22, // 32: management.ManagementService.CreateSchematic:input_type -> management.CreateSchematicRequest
24, // 33: management.ManagementService.GetSupportBundle:input_type -> management.GetSupportBundleRequest
26, // 34: management.ManagementService.ReadAuditLog:input_type -> management.ReadAuditLogRequest
30, // 35: management.ManagementService.MaintenanceUpgrade:input_type -> management.MaintenanceUpgradeRequest
32, // 36: management.ManagementService.GetMachineJoinConfig:input_type -> management.GetMachineJoinConfigRequest
35, // 37: management.ManagementService.CreateJoinToken:input_type -> management.CreateJoinTokenRequest
37, // 38: management.ManagementService.ResetNodeUniqueToken:input_type -> management.ResetNodeUniqueTokenRequest
39, // 39: management.ManagementService.CreateUser:input_type -> management.CreateUserRequest
54, // 40: management.ManagementService.ListUsers:input_type -> google.protobuf.Empty
41, // 41: management.ManagementService.UpdateUser:input_type -> management.UpdateUserRequest
42, // 42: management.ManagementService.DestroyUser:input_type -> management.DestroyUserRequest
4, // 43: management.ManagementService.Kubeconfig:output_type -> management.KubeconfigResponse
5, // 44: management.ManagementService.Talosconfig:output_type -> management.TalosconfigResponse
6, // 45: management.ManagementService.Omniconfig:output_type -> management.OmniconfigResponse
55, // 46: management.ManagementService.MachineLogs:output_type -> common.Data
54, // 47: management.ManagementService.ValidateConfig:output_type -> google.protobuf.Empty
29, // 48: management.ManagementService.ValidateJSONSchema:output_type -> management.ValidateJsonSchemaResponse
11, // 49: management.ManagementService.CreateServiceAccount:output_type -> management.CreateServiceAccountResponse
13, // 50: management.ManagementService.RenewServiceAccount:output_type -> management.RenewServiceAccountResponse
15, // 51: management.ManagementService.ListServiceAccounts:output_type -> management.ListServiceAccountsResponse
54, // 52: management.ManagementService.DestroyServiceAccount:output_type -> google.protobuf.Empty
18, // 53: management.ManagementService.KubernetesUpgradePreChecks:output_type -> management.KubernetesUpgradePreChecksResponse
21, // 54: management.ManagementService.KubernetesSyncManifests:output_type -> management.KubernetesSyncManifestResponse
23, // 55: management.ManagementService.CreateSchematic:output_type -> management.CreateSchematicResponse
25, // 56: management.ManagementService.GetSupportBundle:output_type -> management.GetSupportBundleResponse
27, // 57: management.ManagementService.ReadAuditLog:output_type -> management.ReadAuditLogResponse
31, // 58: management.ManagementService.MaintenanceUpgrade:output_type -> management.MaintenanceUpgradeResponse
33, // 59: management.ManagementService.GetMachineJoinConfig:output_type -> management.GetMachineJoinConfigResponse
36, // 60: management.ManagementService.CreateJoinToken:output_type -> management.CreateJoinTokenResponse
38, // 61: management.ManagementService.ResetNodeUniqueToken:output_type -> management.ResetNodeUniqueTokenResponse
40, // 62: management.ManagementService.CreateUser:output_type -> management.CreateUserResponse
43, // 63: management.ManagementService.ListUsers:output_type -> management.ListUsersResponse
54, // 64: management.ManagementService.UpdateUser:output_type -> google.protobuf.Empty
54, // 65: management.ManagementService.DestroyUser:output_type -> google.protobuf.Empty
43, // [43:66] is the sub-list for method output_type
20, // [20:43] is the sub-list for method input_type
20, // [20:20] is the sub-list for extension type_name
20, // [20:20] is the sub-list for extension extendee
0, // [0:20] is the sub-list for field type_name
51, // 10: management.GetSupportBundleResponse.progress:type_name -> management.GetSupportBundleResponse.Progress
2, // 11: management.ReadAuditLogRequest.order_by_field:type_name -> management.AuditLogOrderByField
3, // 12: management.ReadAuditLogRequest.order_by_dir:type_name -> management.AuditLogOrderByDir
1, // 13: management.ReadAuditLogRequest.event_type:type_name -> management.AuditLogEventType
52, // 14: management.ValidateJsonSchemaResponse.errors:type_name -> management.ValidateJsonSchemaResponse.Error
56, // 15: management.CreateJoinTokenRequest.expiration_time:type_name -> google.protobuf.Timestamp
53, // 16: management.ListUsersResponse.users:type_name -> management.ListUsersResponse.User
48, // 17: management.ListServiceAccountsResponse.ServiceAccount.pgp_public_keys:type_name -> management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey
56, // 18: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.expiration:type_name -> google.protobuf.Timestamp
56, // 19: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.created:type_name -> google.protobuf.Timestamp
56, // 20: management.ListServiceAccountsResponse.ServiceAccount.PgpPublicKey.last_used:type_name -> google.protobuf.Timestamp
52, // 21: management.ValidateJsonSchemaResponse.Error.errors:type_name -> management.ValidateJsonSchemaResponse.Error
54, // 22: management.ListUsersResponse.User.saml_labels:type_name -> management.ListUsersResponse.User.SamlLabelsEntry
19, // 23: management.ManagementService.Kubeconfig:input_type -> management.KubeconfigRequest
12, // 24: management.ManagementService.Talosconfig:input_type -> management.TalosconfigRequest
57, // 25: management.ManagementService.Omniconfig:input_type -> google.protobuf.Empty
10, // 26: management.ManagementService.MachineLogs:input_type -> management.MachineLogsRequest
11, // 27: management.ManagementService.ValidateConfig:input_type -> management.ValidateConfigRequest
31, // 28: management.ManagementService.ValidateJSONSchema:input_type -> management.ValidateJsonSchemaRequest
13, // 29: management.ManagementService.CreateServiceAccount:input_type -> management.CreateServiceAccountRequest
15, // 30: management.ManagementService.RenewServiceAccount:input_type -> management.RenewServiceAccountRequest
57, // 31: management.ManagementService.ListServiceAccounts:input_type -> google.protobuf.Empty
17, // 32: management.ManagementService.DestroyServiceAccount:input_type -> management.DestroyServiceAccountRequest
20, // 33: management.ManagementService.KubernetesUpgradePreChecks:input_type -> management.KubernetesUpgradePreChecksRequest
23, // 34: management.ManagementService.KubernetesSyncManifests:input_type -> management.KubernetesSyncManifestRequest
25, // 35: management.ManagementService.CreateSchematic:input_type -> management.CreateSchematicRequest
27, // 36: management.ManagementService.GetSupportBundle:input_type -> management.GetSupportBundleRequest
29, // 37: management.ManagementService.ReadAuditLog:input_type -> management.ReadAuditLogRequest
33, // 38: management.ManagementService.MaintenanceUpgrade:input_type -> management.MaintenanceUpgradeRequest
35, // 39: management.ManagementService.GetMachineJoinConfig:input_type -> management.GetMachineJoinConfigRequest
38, // 40: management.ManagementService.CreateJoinToken:input_type -> management.CreateJoinTokenRequest
40, // 41: management.ManagementService.ResetNodeUniqueToken:input_type -> management.ResetNodeUniqueTokenRequest
42, // 42: management.ManagementService.CreateUser:input_type -> management.CreateUserRequest
57, // 43: management.ManagementService.ListUsers:input_type -> google.protobuf.Empty
44, // 44: management.ManagementService.UpdateUser:input_type -> management.UpdateUserRequest
45, // 45: management.ManagementService.DestroyUser:input_type -> management.DestroyUserRequest
7, // 46: management.ManagementService.Kubeconfig:output_type -> management.KubeconfigResponse
8, // 47: management.ManagementService.Talosconfig:output_type -> management.TalosconfigResponse
9, // 48: management.ManagementService.Omniconfig:output_type -> management.OmniconfigResponse
58, // 49: management.ManagementService.MachineLogs:output_type -> common.Data
57, // 50: management.ManagementService.ValidateConfig:output_type -> google.protobuf.Empty
32, // 51: management.ManagementService.ValidateJSONSchema:output_type -> management.ValidateJsonSchemaResponse
14, // 52: management.ManagementService.CreateServiceAccount:output_type -> management.CreateServiceAccountResponse
16, // 53: management.ManagementService.RenewServiceAccount:output_type -> management.RenewServiceAccountResponse
18, // 54: management.ManagementService.ListServiceAccounts:output_type -> management.ListServiceAccountsResponse
57, // 55: management.ManagementService.DestroyServiceAccount:output_type -> google.protobuf.Empty
21, // 56: management.ManagementService.KubernetesUpgradePreChecks:output_type -> management.KubernetesUpgradePreChecksResponse
24, // 57: management.ManagementService.KubernetesSyncManifests:output_type -> management.KubernetesSyncManifestResponse
26, // 58: management.ManagementService.CreateSchematic:output_type -> management.CreateSchematicResponse
28, // 59: management.ManagementService.GetSupportBundle:output_type -> management.GetSupportBundleResponse
30, // 60: management.ManagementService.ReadAuditLog:output_type -> management.ReadAuditLogResponse
34, // 61: management.ManagementService.MaintenanceUpgrade:output_type -> management.MaintenanceUpgradeResponse
36, // 62: management.ManagementService.GetMachineJoinConfig:output_type -> management.GetMachineJoinConfigResponse
39, // 63: management.ManagementService.CreateJoinToken:output_type -> management.CreateJoinTokenResponse
41, // 64: management.ManagementService.ResetNodeUniqueToken:output_type -> management.ResetNodeUniqueTokenResponse
43, // 65: management.ManagementService.CreateUser:output_type -> management.CreateUserResponse
46, // 66: management.ManagementService.ListUsers:output_type -> management.ListUsersResponse
57, // 67: management.ManagementService.UpdateUser:output_type -> google.protobuf.Empty
57, // 68: management.ManagementService.DestroyUser:output_type -> google.protobuf.Empty
46, // [46:69] is the sub-list for method output_type
23, // [23:46] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
}
func init() { file_omni_management_management_proto_init() }
@ -3155,7 +3433,7 @@ func file_omni_management_management_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_omni_management_management_proto_rawDesc), len(file_omni_management_management_proto_rawDesc)),
NumEnums: 4,
NumEnums: 7,
NumMessages: 48,
NumExtensions: 0,
NumServices: 1,

View File

@ -112,7 +112,7 @@ message KubernetesUpgradePreChecksResponse {
}
message KubernetesSSAOptions {
enum InventoryPolicy {
enum InventoryPolicy {
MUST_MATCH = 0;
ADOPT_IF_NO_INVENTORY = 1;
ADOPT_ALL = 2;
@ -199,10 +199,45 @@ message GetSupportBundleResponse {
bytes bundle_data = 2;
}
enum AuditLogEventType {
AUDIT_LOG_EVENT_TYPE_UNSPECIFIED = 0;
AUDIT_LOG_EVENT_TYPE_CREATE = 1;
AUDIT_LOG_EVENT_TYPE_UPDATE = 2;
AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS = 3;
AUDIT_LOG_EVENT_TYPE_DESTROY = 4;
AUDIT_LOG_EVENT_TYPE_TEARDOWN = 5;
AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS = 6;
AUDIT_LOG_EVENT_TYPE_K8S_ACCESS = 7;
}
enum AuditLogOrderByField {
AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED = 0;
AUDIT_LOG_ORDER_BY_FIELD_DATE = 1;
AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE = 2;
AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE = 3;
AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID = 4;
AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID = 5;
AUDIT_LOG_ORDER_BY_FIELD_ACTOR = 6;
}
enum AuditLogOrderByDir {
AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED = 0;
AUDIT_LOG_ORDER_BY_DIR_ASC = 1;
AUDIT_LOG_ORDER_BY_DIR_DESC = 2;
}
// specifies start and end time (inclusive range) in <year>-<month>-<day> format. We pass time as string to avoid timezone issues.
message ReadAuditLogRequest {
string start_time = 1;
string end_time = 2;
AuditLogOrderByField order_by_field = 3;
AuditLogOrderByDir order_by_dir = 4;
string search = 5;
AuditLogEventType event_type = 6;
string resource_type = 7;
string resource_id = 8;
string cluster_id = 9;
string actor = 10;
}
message ReadAuditLogResponse {

View File

@ -572,6 +572,14 @@ func (m *ReadAuditLogRequest) CloneVT() *ReadAuditLogRequest {
r := new(ReadAuditLogRequest)
r.StartTime = m.StartTime
r.EndTime = m.EndTime
r.OrderByField = m.OrderByField
r.OrderByDir = m.OrderByDir
r.Search = m.Search
r.EventType = m.EventType
r.ResourceType = m.ResourceType
r.ResourceId = m.ResourceId
r.ClusterId = m.ClusterId
r.Actor = m.Actor
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
@ -1658,6 +1666,30 @@ func (this *ReadAuditLogRequest) EqualVT(that *ReadAuditLogRequest) bool {
if this.EndTime != that.EndTime {
return false
}
if this.OrderByField != that.OrderByField {
return false
}
if this.OrderByDir != that.OrderByDir {
return false
}
if this.Search != that.Search {
return false
}
if this.EventType != that.EventType {
return false
}
if this.ResourceType != that.ResourceType {
return false
}
if this.ResourceId != that.ResourceId {
return false
}
if this.ClusterId != that.ClusterId {
return false
}
if this.Actor != that.Actor {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
@ -3613,6 +3645,56 @@ func (m *ReadAuditLogRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Actor) > 0 {
i -= len(m.Actor)
copy(dAtA[i:], m.Actor)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Actor)))
i--
dAtA[i] = 0x52
}
if len(m.ClusterId) > 0 {
i -= len(m.ClusterId)
copy(dAtA[i:], m.ClusterId)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterId)))
i--
dAtA[i] = 0x4a
}
if len(m.ResourceId) > 0 {
i -= len(m.ResourceId)
copy(dAtA[i:], m.ResourceId)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceId)))
i--
dAtA[i] = 0x42
}
if len(m.ResourceType) > 0 {
i -= len(m.ResourceType)
copy(dAtA[i:], m.ResourceType)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceType)))
i--
dAtA[i] = 0x3a
}
if m.EventType != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EventType))
i--
dAtA[i] = 0x30
}
if len(m.Search) > 0 {
i -= len(m.Search)
copy(dAtA[i:], m.Search)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Search)))
i--
dAtA[i] = 0x2a
}
if m.OrderByDir != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OrderByDir))
i--
dAtA[i] = 0x20
}
if m.OrderByField != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OrderByField))
i--
dAtA[i] = 0x18
}
if len(m.EndTime) > 0 {
i -= len(m.EndTime)
copy(dAtA[i:], m.EndTime)
@ -5076,6 +5158,35 @@ func (m *ReadAuditLogRequest) SizeVT() (n int) {
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.OrderByField != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.OrderByField))
}
if m.OrderByDir != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.OrderByDir))
}
l = len(m.Search)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.EventType != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.EventType))
}
l = len(m.ResourceType)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
l = len(m.ResourceId)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
l = len(m.ClusterId)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
l = len(m.Actor)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -9092,6 +9203,223 @@ func (m *ReadAuditLogRequest) UnmarshalVT(dAtA []byte) error {
}
m.EndTime = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field OrderByField", wireType)
}
m.OrderByField = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.OrderByField |= AuditLogOrderByField(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field OrderByDir", wireType)
}
m.OrderByDir = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.OrderByDir |= AuditLogOrderByDir(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Search", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Search = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field EventType", wireType)
}
m.EventType = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.EventType |= AuditLogEventType(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResourceType", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ResourceType = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ResourceId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ClusterId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 10:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Actor", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Actor = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])

View File

@ -275,12 +275,9 @@ func (client *Client) GetSupportBundle(ctx context.Context, cluster string, prog
}
// ReadAuditLog reads the audit log from the backend.
func (client *Client) ReadAuditLog(ctx context.Context, start, end string) iter.Seq2[*management.ReadAuditLogResponse, error] {
func (client *Client) ReadAuditLog(ctx context.Context, req *management.ReadAuditLogRequest) iter.Seq2[*management.ReadAuditLogResponse, error] {
return func(yield func(*management.ReadAuditLogResponse, error) bool) {
streamingResponse, err := client.conn.ReadAuditLog(ctx, &management.ReadAuditLogRequest{
StartTime: start,
EndTime: end,
})
streamingResponse, err := client.conn.ReadAuditLog(ctx, req)
if err != nil {
yield(nil, err)

View File

@ -6,14 +6,30 @@ package omnictl
import (
"context"
"fmt"
"maps"
"os"
"slices"
"strings"
"github.com/spf13/cobra"
"github.com/siderolabs/omni/client/api/omni/management"
"github.com/siderolabs/omni/client/pkg/client"
"github.com/siderolabs/omni/client/pkg/omnictl/internal/access"
)
var auditLogFlags struct {
search string
eventType enumFlag[management.AuditLogEventType]
orderByField enumFlag[management.AuditLogOrderByField]
orderByDir enumFlag[management.AuditLogOrderByDir]
resourceType string
resourceID string
clusterID string
actor string
}
// auditLog represents audit-log command.
var auditLog = &cobra.Command{
Use: "audit-log [start] [end]",
@ -25,7 +41,18 @@ var auditLog = &cobra.Command{
end := safeGet(arg, 1)
return access.WithClient(func(ctx context.Context, client *client.Client, _ access.ServerInfo) error {
for resp, err := range client.Management().ReadAuditLog(ctx, start, end) {
for resp, err := range client.Management().ReadAuditLog(ctx, &management.ReadAuditLogRequest{
StartTime: start,
EndTime: end,
Search: auditLogFlags.search,
EventType: auditLogFlags.eventType.value,
OrderByField: auditLogFlags.orderByField.value,
OrderByDir: auditLogFlags.orderByDir.value,
ResourceType: auditLogFlags.resourceType,
ResourceId: auditLogFlags.resourceID,
ClusterId: auditLogFlags.clusterID,
Actor: auditLogFlags.actor,
}) {
if err != nil {
return err
}
@ -41,6 +68,42 @@ var auditLog = &cobra.Command{
},
}
// enumFlag is a pflag.Value implementation for proto enum types. It validates
// the input against a fixed set of allowed string values and stores the
// corresponding proto enum value.
type enumFlag[T ~int32] struct {
value T
allowed map[string]T
}
// String implements pflag.Value.
func (f *enumFlag[T]) String() string {
for k, v := range f.allowed {
if v == f.value {
return k
}
}
return ""
}
// Set implements pflag.Value.
func (f *enumFlag[T]) Set(s string) error {
v, ok := f.allowed[s]
if !ok {
return fmt.Errorf("must be one of: %s", strings.Join(slices.Sorted(maps.Keys(f.allowed)), ", "))
}
f.value = v
return nil
}
// Type implements pflag.Value.
func (f *enumFlag[T]) Type() string {
return strings.Join(slices.Sorted(maps.Keys(f.allowed)), "|")
}
func safeGet[T any](slc []T, pos int) T {
if pos < len(slc) {
return slc[pos]
@ -50,5 +113,44 @@ func safeGet[T any](slc []T, pos int) T {
}
func init() {
auditLogFlags.eventType = enumFlag[management.AuditLogEventType]{
allowed: map[string]management.AuditLogEventType{
"create": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_CREATE,
"update": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE,
"update_with_conflicts": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS,
"destroy": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_DESTROY,
"teardown": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TEARDOWN,
"talos_access": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS,
"k8s_access": management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_K8S_ACCESS,
},
}
auditLogFlags.orderByField = enumFlag[management.AuditLogOrderByField]{
allowed: map[string]management.AuditLogOrderByField{
"event_ts_ms": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_DATE,
"event_type": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE,
"resource_type": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE,
"resource_id": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID,
"cluster_id": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID,
"actor": management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_ACTOR,
},
}
auditLogFlags.orderByDir = enumFlag[management.AuditLogOrderByDir]{
allowed: map[string]management.AuditLogOrderByDir{
"asc": management.AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_ASC,
"desc": management.AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_DESC,
},
}
auditLog.Flags().StringVar(&auditLogFlags.search, "search", "", "filter events by a search string")
auditLog.Flags().Var(&auditLogFlags.eventType, "event-type", "filter events by event type")
auditLog.Flags().Var(&auditLogFlags.orderByField, "order-by-field", "field to sort results by")
auditLog.Flags().Var(&auditLogFlags.orderByDir, "order-by-dir", "sort direction")
auditLog.Flags().StringVar(&auditLogFlags.resourceType, "resource-type", "", "filter events by resource type")
auditLog.Flags().StringVar(&auditLogFlags.resourceID, "resource-id", "", "filter events by resource ID")
auditLog.Flags().StringVar(&auditLogFlags.clusterID, "cluster-id", "", "filter events by cluster ID")
auditLog.Flags().StringVar(&auditLogFlags.actor, "actor", "", "filter events by actor email")
RootCmd.AddCommand(auditLog)
}

View File

@ -18,6 +18,7 @@
"@vueuse/components": "^14.2.1",
"@vueuse/core": "^14.2.1",
"@vueuse/integrations": "^14.2.1",
"@vueuse/router": "^14.2.1",
"apexcharts": "^5.10.6",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
@ -4149,6 +4150,22 @@
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/router": {
"version": "14.2.1",
"resolved": "https://registry.npmjs.org/@vueuse/router/-/router-14.2.1.tgz",
"integrity": "sha512-SbZfJe+qn5bj78zNOXT4nYbnp8OIFMyAsdcJb4Y0y9vXi1TsOfglF+YIazi5DPO2lk6/ZukpN5DEQe6KrNOjMw==",
"license": "MIT",
"dependencies": {
"@vueuse/shared": "14.2.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0",
"vue-router": "^4.0.0 || ^5.0.0"
}
},
"node_modules/@vueuse/shared": {
"version": "14.2.1",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.2.1.tgz",

View File

@ -35,6 +35,7 @@
"@vueuse/components": "^14.2.1",
"@vueuse/core": "^14.2.1",
"@vueuse/integrations": "^14.2.1",
"@vueuse/router": "^14.2.1",
"apexcharts": "^5.10.6",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",

View File

@ -17,6 +17,33 @@ export enum SchematicBootloader {
BOOT_GRUB = 3,
}
export enum AuditLogEventType {
AUDIT_LOG_EVENT_TYPE_UNSPECIFIED = 0,
AUDIT_LOG_EVENT_TYPE_CREATE = 1,
AUDIT_LOG_EVENT_TYPE_UPDATE = 2,
AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS = 3,
AUDIT_LOG_EVENT_TYPE_DESTROY = 4,
AUDIT_LOG_EVENT_TYPE_TEARDOWN = 5,
AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS = 6,
AUDIT_LOG_EVENT_TYPE_K8S_ACCESS = 7,
}
export enum AuditLogOrderByField {
AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED = 0,
AUDIT_LOG_ORDER_BY_FIELD_DATE = 1,
AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE = 2,
AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE = 3,
AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID = 4,
AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID = 5,
AUDIT_LOG_ORDER_BY_FIELD_ACTOR = 6,
}
export enum AuditLogOrderByDir {
AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED = 0,
AUDIT_LOG_ORDER_BY_DIR_ASC = 1,
AUDIT_LOG_ORDER_BY_DIR_DESC = 2,
}
export enum KubernetesSSAOptionsInventoryPolicy {
MUST_MATCH = 0,
ADOPT_IF_NO_INVENTORY = 1,
@ -191,6 +218,14 @@ export type GetSupportBundleResponse = {
export type ReadAuditLogRequest = {
start_time?: string
end_time?: string
order_by_field?: AuditLogOrderByField
order_by_dir?: AuditLogOrderByDir
search?: string
event_type?: AuditLogEventType
resource_type?: string
resource_id?: string
cluster_id?: string
actor?: string
}
export type ReadAuditLogResponse = {

View File

@ -11,13 +11,14 @@ const decoder = new TextDecoder()
<script setup lang="ts">
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from 'reka-ui'
import { computed } from 'vue'
import WordHighlighter from 'vue-word-highlighter'
import CodeBlock from '@/components/CodeBlock/CodeBlock.vue'
import TIcon from '@/components/Icon/TIcon.vue'
import { formatISO } from '@/methods/time'
import type { AuditLogEvent } from '@/pages/(authenticated)/settings/audit-logs.vue'
const { data } = defineProps<{ data: Uint8Array<ArrayBuffer> }>()
const { data } = defineProps<{ data: Uint8Array<ArrayBuffer>; search: string }>()
const open = defineModel<boolean>({ default: false })
@ -72,29 +73,46 @@ function toggleRow() {
</div>
<div role="cell">
<span class="resource-label" :class="getLabelClassForEvent(item.event_type)">
{{ item.event_type.toUpperCase() }}
</span>
<WordHighlighter
v-if="item.event_type.toUpperCase()"
:query="search"
:text-to-highlight="item.event_type.toUpperCase()"
highlight-class="bg-naturals-n14"
class="resource-label"
:class="getLabelClassForEvent(item.event_type)"
/>
</div>
<div role="cell" class="truncate text-naturals-n10">
{{ item.resource_type }}
<WordHighlighter
:query="search"
:text-to-highlight="item.resource_type"
highlight-class="bg-naturals-n14"
class="text-naturals-n14"
/>
</div>
<div role="cell" class="min-w-20 space-x-2 truncate whitespace-nowrap">
<span class="text-naturals-n14">
{{
item.event_data.session.role ? item.event_data.session.role : 'System / Service Account'
}}
</span>
<WordHighlighter
v-if="item.event_data.session.role"
:query="search"
:text-to-highlight="item.event_data.session.role"
highlight-class="bg-naturals-n14"
class="text-naturals-n14"
/>
<span class="text-naturals-n10">
{{
<span v-else class="text-naturals-n14">System / Service Account</span>
<WordHighlighter
:query="search"
:text-to-highlight="
item.event_data.session.email
? item.event_data.session.email
: item.event_data.session.user_agent
}}
</span>
"
highlight-class="bg-naturals-n14"
class="text-naturals-n10"
/>
</div>
</CollapsibleTrigger>
@ -103,7 +121,14 @@ function toggleRow() {
class="collapsible-content col-span-full overflow-hidden group-hover/root:bg-white/5"
>
<div role="cell" class="px-2 pb-2">
<CodeBlock :code="JSON.stringify(item, null, 2)" />
<CodeBlock>
<WordHighlighter
:query="search"
:text-to-highlight="JSON.stringify(item, null, 2)"
highlight-class="bg-naturals-n14"
class="text-naturals-n10"
/>
</CodeBlock>
</div>
</CollapsibleContent>
</CollapsibleRoot>

View File

@ -26,7 +26,9 @@ const { code = '', buttonAttrs } = defineProps<Props>()
</div>
<div class="p-1">
<pre class="overflow-auto px-3 py-1 font-mono text-xs/6 whitespace-pre">{{ code }}</pre>
<pre
class="overflow-auto px-3 py-1 font-mono text-xs/6 whitespace-pre"
><slot v-if="!code"></slot><template v-else>{{ code }}</template></pre>
</div>
</div>
</template>

View File

@ -37,6 +37,7 @@ import TIcon from '@/components/Icon/TIcon.vue'
interface Props extends DateRangePickerRootProps {
title: string
hiddenTitle?: boolean
inlineTitle?: boolean
}
// eslint-disable-next-line vue/define-props-destructuring
@ -50,7 +51,7 @@ const id = useId()
</script>
<template>
<div class="inline-flex flex-col gap-2">
<div class="inline-flex gap-2" :class="inlineTitle ? 'items-center' : 'flex-col'">
<Label class="text-sm text-naturals-n14" :class="{ 'sr-only': hiddenTitle }" :for="id">
{{ title }}
</Label>

View File

@ -32,6 +32,8 @@ export interface AuditLogMsg {
<script setup lang="ts">
import { getLocalTimeZone, today } from '@internationalized/date'
import { useVirtualizer } from '@tanstack/vue-virtual'
import { refDebounced } from '@vueuse/core'
import { useRouteQuery } from '@vueuse/router'
import { differenceInDays } from 'date-fns'
import { type DateRange } from 'reka-ui'
import {
@ -44,12 +46,15 @@ import {
watch,
} from 'vue'
import { AuditLogOrderByDir, AuditLogOrderByField } from '@/api/omni/management/management.pb'
import AuditLogItem from '@/components/AuditLogs/AuditLogItem.vue'
import TButton from '@/components/Button/TButton.vue'
import DateRangePicker from '@/components/DateRangePicker/DateRangePicker.vue'
import TIcon from '@/components/Icon/TIcon.vue'
import PageContainer from '@/components/PageContainer/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
import TAlert from '@/components/TAlert.vue'
import TInput from '@/components/TInput/TInput.vue'
import { useReadAuditLog } from '@/views/Settings/useReadAuditLog'
definePage({ name: 'AuditLogs' })
@ -59,10 +64,44 @@ const dateRange = shallowRef<DateRange>({
end: today(getLocalTimeZone()).add({ days: 1 }),
})
const { data, loading } = useReadAuditLog(() => ({
const searchInput = useRouteQuery('search', '')
const search = refDebounced(searchInput, 500)
const orderByField = useRouteQuery(
'orderByField',
AuditLogOrderByField.AUDIT_LOG_ORDER_BY_FIELD_DATE,
{ transform: (v) => Number(v) as AuditLogOrderByField },
)
const orderByDir = useRouteQuery('orderByDir', AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_ASC, {
transform: (v) => Number(v) as AuditLogOrderByDir,
})
const tableHeaders = [
{ label: 'Timestamp', value: AuditLogOrderByField.AUDIT_LOG_ORDER_BY_FIELD_DATE },
{ label: 'Type', value: AuditLogOrderByField.AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE },
{ label: 'Resource', value: AuditLogOrderByField.AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE },
{ label: 'Actor', value: AuditLogOrderByField.AUDIT_LOG_ORDER_BY_FIELD_ACTOR },
]
function toggleSort(value: AuditLogOrderByField) {
if (orderByField.value !== value) {
orderByField.value = value
orderByDir.value = AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_ASC
} else if (orderByDir.value === AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_ASC) {
orderByDir.value = AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_DESC
} else {
orderByDir.value = AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_ASC
}
}
const { data, loading, error } = useReadAuditLog(() => ({
options: {
start_time: dateRange.value.start?.toString(),
end_time: dateRange.value.end?.toString(),
search: search.value,
order_by_field: orderByField.value,
order_by_dir: orderByDir.value,
},
}))
@ -118,8 +157,12 @@ function measureElement(el: Element | ComponentPublicInstance | null) {
</TButton>
</div>
<TAlert v-if="error" type="error" title="Error" class="mb-4">{{ error.message }}</TAlert>
<TInput v-model="searchInput" class="mb-2" title="Search" />
<div class="mb-4 flex justify-end gap-2">
<DateRangePicker v-model="dateRange" title="Date range" hidden-title />
<DateRangePicker v-model="dateRange" title="Date range" inline-title />
</div>
<TAlert
@ -145,10 +188,26 @@ function measureElement(el: Element | ComponentPublicInstance | null) {
class="grid shrink-0 grid-cols-[36px_160px_180px_220px_1fr] gap-x-0.5 bg-naturals-n2 px-2 text-left"
>
<div role="columnheader" aria-hidden="true" class="py-2 uppercase"></div>
<div role="columnheader" class="py-2 uppercase">Timestamp</div>
<div role="columnheader" class="py-2 uppercase">Type</div>
<div role="columnheader" class="py-2 uppercase">Resource</div>
<div role="columnheader" class="py-2 uppercase">Actor</div>
<div
v-for="{ label, value } in tableHeaders"
:key="value"
role="columnheader"
class="cursor-pointer py-2 uppercase transition-colors select-none hover:text-naturals-n11 active:text-naturals-n9"
@click="toggleSort(value)"
>
<span class="inline-flex items-center">
{{ label }}
<TIcon
class="size-5 text-naturals-n10 transition-transform"
icon="dropdown"
:class="{
invisible: orderByField !== value,
'rotate-180': orderByDir === AuditLogOrderByDir.AUDIT_LOG_ORDER_BY_DIR_ASC,
}"
/>
</span>
</div>
</div>
<div ref="scrollContainer" role="rowgroup" class="min-h-0 flex-1 overflow-y-auto">
@ -167,6 +226,7 @@ function measureElement(el: Element | ComponentPublicInstance | null) {
<AuditLogItem
:data="data[vRow.index]"
:model-value="expandedRows.has(vRow.index)"
:search
@update:model-value="toggleRow(vRow.index)"
/>
</div>

View File

@ -507,23 +507,35 @@ func (s *managementServer) KubernetesUpgradePreChecks(ctx context.Context, req *
func (s *managementServer) ReadAuditLog(req *management.ReadAuditLogRequest, srv grpc.ServerStreamingServer[management.ReadAuditLogResponse]) error {
ctx := srv.Context()
if _, err := s.authCheckGRPC(ctx, auth.WithRole(role.Admin)); err != nil {
_, err := s.authCheckGRPC(ctx, auth.WithRole(role.Admin))
if err != nil {
return err
}
now := time.Now()
start, err := parseTime(req.GetStartTime(), now.AddDate(0, 0, -29))
filters := auditlog.ReadFilters{
OrderByField: auditLogOrderByField(req.GetOrderByField()),
OrderByDir: auditLogOrderByDir(req.GetOrderByDir()),
Search: req.GetSearch(),
EventType: auditLogEventType(req.GetEventType()),
ResourceType: req.GetResourceType(),
ResourceID: req.GetResourceId(),
ClusterID: req.GetClusterId(),
Actor: req.GetActor(),
}
filters.Start, err = parseTime(req.GetStartTime(), now.AddDate(0, 0, -29))
if err != nil {
return status.Errorf(codes.InvalidArgument, "invalid start time: %v", err)
}
end, err := parseTime(req.GetEndTime(), now)
filters.End, err = parseTime(req.GetEndTime(), now)
if err != nil {
return status.Errorf(codes.InvalidArgument, "invalid end time: %v", err)
}
rdr, err := s.auditor.Reader(ctx, start, end)
rdr, err := s.auditor.Reader(ctx, filters)
if err != nil {
return err
}
@ -819,5 +831,62 @@ func generateDest(apiurl string) (string, error) {
// AuditLogger is an interface for reading the audit log.
type AuditLogger interface {
Reader(ctx context.Context, start, end time.Time) (auditlog.Reader, error)
Reader(ctx context.Context, filters auditlog.ReadFilters) (auditlog.Reader, error)
}
func auditLogOrderByField(f management.AuditLogOrderByField) auditlog.OrderByField {
switch f {
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_UNSPECIFIED:
return auditlog.OrderByFieldDate
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_DATE:
return auditlog.OrderByFieldDate
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_EVENT_TYPE:
return auditlog.OrderByFieldEventType
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_TYPE:
return auditlog.OrderByFieldResourceType
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_RESOURCE_ID:
return auditlog.OrderByFieldResourceID
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_CLUSTER_ID:
return auditlog.OrderByFieldClusterID
case management.AuditLogOrderByField_AUDIT_LOG_ORDER_BY_FIELD_ACTOR:
return auditlog.OrderByFieldActor
}
return auditlog.OrderByFieldDate
}
func auditLogOrderByDir(d management.AuditLogOrderByDir) auditlog.OrderByDir {
switch d {
case management.AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_UNSPECIFIED:
return auditlog.OrderByDirASC
case management.AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_ASC:
return auditlog.OrderByDirASC
case management.AuditLogOrderByDir_AUDIT_LOG_ORDER_BY_DIR_DESC:
return auditlog.OrderByDirDESC
}
return auditlog.OrderByDirASC
}
func auditLogEventType(e management.AuditLogEventType) auditlog.EventType {
switch e {
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UNSPECIFIED:
return auditlog.EventTypeUnspecified
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_CREATE:
return auditlog.EventTypeCreate
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE:
return auditlog.EventTypeUpdate
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_UPDATE_WITH_CONFLICTS:
return auditlog.EventTypeUpdateWithConflicts
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_DESTROY:
return auditlog.EventTypeDestroy
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TEARDOWN:
return auditlog.EventTypeTeardown
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_TALOS_ACCESS:
return auditlog.EventTypeTalosAccess
case management.AuditLogEventType_AUDIT_LOG_EVENT_TYPE_K8S_ACCESS:
return auditlog.EventTypeK8SAccess
}
return auditlog.EventTypeUnspecified
}

View File

@ -30,7 +30,7 @@ import (
type Logger interface {
Write(ctx context.Context, event auditlog.Event) error
Remove(ctx context.Context, start, end time.Time) error
Reader(ctx context.Context, start, end time.Time) (auditlog.Reader, error)
Reader(ctx context.Context, filters auditlog.ReadFilters) (auditlog.Reader, error)
}
// LogOption configures optional Log behavior.
@ -107,8 +107,8 @@ func hooksResourceTypes[T any](l *Log, m map[resource.Type]T) []resource.Type {
// Reader reads the audit log file by file, oldest to newest within the given time range. The time range
// is inclusive, and truncated to the day.
func (l *Log) Reader(ctx context.Context, start, end time.Time) (auditlog.Reader, error) {
return l.auditLogger.Reader(ctx, start, end)
func (l *Log) Reader(ctx context.Context, filters auditlog.ReadFilters) (auditlog.Reader, error) {
return l.auditLogger.Reader(ctx, filters)
}
// LogCreate logs the resource creation if there is a hook for this type.

View File

@ -108,7 +108,7 @@ func TestAudit(t *testing.T) {
action(t)
}
rdr, err := l.Reader(t.Context(), time.Time{}, time.Now().Add(5*time.Second))
rdr, err := l.Reader(t.Context(), auditlog.ReadFilters{End: time.Now().Add(5 * time.Second)})
require.NoError(t, err)
t.Cleanup(func() {
@ -172,7 +172,7 @@ func TestPhaseChangeIsAudited(t *testing.T) {
require.NoError(t, updateFn(createCtx(), res, newRes))
// Read all events and verify we got a teardown event.
rdr, err := l.Reader(t.Context(), time.Time{}, time.Now().Add(5*time.Second))
rdr, err := l.Reader(t.Context(), auditlog.ReadFilters{End: time.Now().Add(5 * time.Second)})
require.NoError(t, err)
t.Cleanup(func() {

View File

@ -18,6 +18,80 @@ type Reader interface {
Read() ([]byte, error)
}
// EventType represents the type of audit log event.
type EventType int
const (
EventTypeUnspecified EventType = iota
EventTypeCreate // "create"
EventTypeUpdate // "update"
EventTypeUpdateWithConflicts // "update_with_conflicts"
EventTypeDestroy // "destroy"
EventTypeTeardown // "teardown"
EventTypeTalosAccess // "talos_access"
EventTypeK8SAccess // "k8s_access"
)
// SQLString returns the string stored in the database for this event type.
func (e EventType) SQLString() string {
switch e {
case EventTypeUnspecified:
return ""
case EventTypeCreate:
return "create"
case EventTypeUpdate:
return "update"
case EventTypeUpdateWithConflicts:
return "update_with_conflicts"
case EventTypeDestroy:
return "destroy"
case EventTypeTeardown:
return "teardown"
case EventTypeTalosAccess:
return "talos_access"
case EventTypeK8SAccess:
return "k8s_access"
}
return ""
}
// OrderByField represents the audit log field to sort by.
type OrderByField int
const (
OrderByFieldUnspecified OrderByField = iota
OrderByFieldDate
OrderByFieldEventType
OrderByFieldResourceType
OrderByFieldResourceID
OrderByFieldClusterID
OrderByFieldActor
)
// OrderByDir represents the sort direction for audit log queries.
type OrderByDir int
const (
OrderByDirUnspecified OrderByDir = iota
OrderByDirASC
OrderByDirDESC
)
// ReadFilters holds optional filters for reading audit log events.
type ReadFilters struct {
Start time.Time
End time.Time
Search string
ResourceType string
ResourceID string
ClusterID string
Actor string
EventType EventType
OrderByField OrderByField
OrderByDir OrderByDir
}
type Event struct {
Data *Data `json:"event_data,omitempty"`
Type string `json:"event_type,omitempty"`

View File

@ -361,15 +361,69 @@ func (s *Store) removeBySize(conn *zombiesqlite.Conn) error {
return nil
}
func (s *Store) Reader(ctx context.Context, start, end time.Time) (auditlog.Reader, error) {
func (s *Store) Reader(ctx context.Context, filters auditlog.ReadFilters) (auditlog.Reader, error) {
// we take the connection here, but it will be released in logReader.Close()
conn, err := s.db.Take(ctx)
if err != nil {
return nil, fmt.Errorf("failed to take connection from pool: %w", err)
}
query := fmt.Sprintf(`SELECT %s, %s, %s, %s, %s FROM %s WHERE %s >= $start AND %s <= $end ORDER BY %s ASC, %s ASC`, eventTypeColumn,
resourceTypeColumn, resourceIDColumn, eventTSMillisColumn, eventDataColumn, TableName, eventTSMillisColumn, eventTSMillisColumn, eventTSMillisColumn, idColumn)
conditions := []string{
fmt.Sprintf("%s >= $start", eventTSMillisColumn),
fmt.Sprintf("%s <= $end", eventTSMillisColumn),
}
if filters.Search != "" {
searchCols := []string{
eventTypeColumn,
resourceTypeColumn,
actorEmailColumn,
resourceIDColumn,
clusterIDColumn,
fmt.Sprintf("CAST(%s AS TEXT)", eventDataColumn),
}
orParts := make([]string, 0, len(searchCols))
for _, col := range searchCols {
orParts = append(orParts, fmt.Sprintf("%s LIKE '%%' || $search || '%%'", col))
}
conditions = append(conditions, "("+strings.Join(orParts, " OR ")+")")
}
eventTypeSQLStr := filters.EventType.SQLString()
if eventTypeSQLStr != "" {
conditions = append(conditions, fmt.Sprintf("%s = $event_type", eventTypeColumn))
}
if filters.ResourceType != "" {
conditions = append(conditions, fmt.Sprintf("%s = $resource_type", resourceTypeColumn))
}
if filters.ResourceID != "" {
conditions = append(conditions, fmt.Sprintf("%s = $resource_id", resourceIDColumn))
}
if filters.ClusterID != "" {
conditions = append(conditions, fmt.Sprintf("%s = $cluster_id", clusterIDColumn))
}
if filters.Actor != "" {
conditions = append(conditions, fmt.Sprintf("%s = $actor", actorEmailColumn))
}
orderByCol := orderByFieldColumn(filters.OrderByField)
orderByDir := orderByDirSQL(filters.OrderByDir)
query := fmt.Sprintf(
`SELECT %s, %s, %s, %s, %s FROM %s WHERE %s ORDER BY %s %s, %s ASC`,
eventTypeColumn, resourceTypeColumn, resourceIDColumn, eventTSMillisColumn, eventDataColumn,
TableName,
strings.Join(conditions, " AND "),
orderByCol, orderByDir,
idColumn,
)
q, err := sqlitexx.NewQuery(conn, query)
if err != nil {
@ -379,8 +433,14 @@ func (s *Store) Reader(ctx context.Context, start, end time.Time) (auditlog.Read
}
it := q.
BindInt64("$start", start.UnixMilli()).
BindInt64("$end", end.UnixMilli()).
BindInt64("$start", filters.Start.UnixMilli()).
BindInt64("$end", filters.End.UnixMilli()).
BindStringIfSet("$search", filters.Search).
BindStringIfSet("$event_type", eventTypeSQLStr).
BindStringIfSet("$resource_type", filters.ResourceType).
BindStringIfSet("$resource_id", filters.ResourceID).
BindStringIfSet("$cluster_id", filters.ClusterID).
BindStringIfSet("$actor", filters.Actor).
QueryIter()
next, stop := iter.Pull2(it)
@ -490,6 +550,35 @@ func (l *logReader) Read() ([]byte, error) {
return append(marshaled, '\n'), nil
}
func orderByFieldColumn(f auditlog.OrderByField) string {
switch f {
case auditlog.OrderByFieldUnspecified:
return eventTSMillisColumn
case auditlog.OrderByFieldDate:
return eventTSMillisColumn
case auditlog.OrderByFieldEventType:
return eventTypeColumn
case auditlog.OrderByFieldResourceType:
return resourceTypeColumn
case auditlog.OrderByFieldResourceID:
return resourceIDColumn
case auditlog.OrderByFieldClusterID:
return clusterIDColumn
case auditlog.OrderByFieldActor:
return actorEmailColumn
}
return eventTSMillisColumn
}
func orderByDirSQL(d auditlog.OrderByDir) string {
if d == auditlog.OrderByDirDESC {
return "DESC"
}
return "ASC"
}
func extractClusterID(d *auditlog.Data) string {
switch {
case d.Cluster != nil:

View File

@ -71,7 +71,7 @@ func TestReadWrite(t *testing.T) {
t.Run("read all", func(t *testing.T) {
t.Parallel()
rdr, err := store.Reader(ctx, time.Time{}, time.Now().Add(time.Hour))
rdr, err := store.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -124,7 +124,7 @@ func TestRemove(t *testing.T) {
require.NoError(t, err)
// 3. Verify only the last event remains
rdr, err := store.Reader(ctx, time.Time{}, time.Now().Add(24*time.Hour))
rdr, err := store.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(24 * time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -172,7 +172,7 @@ func TestRemoveByRetentionPeriod(t *testing.T) {
require.NoError(t, err)
// Verify only the 3 recent events remain.
rdr, err := store.Reader(ctx, time.Time{}, now.Add(time.Hour))
rdr, err := store.Reader(ctx, auditlog.ReadFilters{End: now.Add(time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -250,7 +250,7 @@ func TestRemoveByMaxSize(t *testing.T) {
}))
// Verify some events were deleted.
rdr, err := sizedStore.Reader(ctx, time.Time{}, time.Now().Add(24*time.Hour))
rdr, err := sizedStore.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(24 * time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -293,7 +293,7 @@ func TestRemoveByMaxSizeUnlimited(t *testing.T) {
require.NoError(t, store.Write(ctx, evt))
}
rdr, err := store.Reader(ctx, time.Time{}, time.Now().Add(24*time.Hour))
rdr, err := store.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(24 * time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -346,7 +346,7 @@ func TestRemoveByMaxSizeBatchCap(t *testing.T) {
// Count remaining rows. The batch cap is 1000, so roughly 1000 should have been
// deleted, leaving ~501 rows (1500 - 1000 + 1 trigger event).
rdr, err := sizedStore.Reader(ctx, time.Time{}, time.Now().Add(24*time.Hour))
rdr, err := sizedStore.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(24 * time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -415,7 +415,7 @@ func TestRemoveBatching(t *testing.T) {
require.NoError(t, store.Remove(ctx, rangeStart, rangeEnd))
// Verify only out-of-range events remain.
rdr, err := store.Reader(ctx, time.Time{}, time.Now().Add(24*time.Hour))
rdr, err := store.Reader(ctx, auditlog.ReadFilters{End: time.Now().Add(24 * time.Hour)})
require.NoError(t, err)
t.Cleanup(func() {
@ -494,7 +494,7 @@ func TestReaderParameters(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
rdr, err := store.Reader(ctx, tt.start, tt.end)
rdr, err := store.Reader(ctx, auditlog.ReadFilters{Start: tt.start, End: tt.end})
require.NoError(t, err)
t.Cleanup(func() {
@ -600,6 +600,167 @@ func TestExtractedColumns(t *testing.T) {
}
}
func TestReaderFilters(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second)
t.Cleanup(cancel)
logger := zaptest.NewLogger(t)
store, _ := setupStore(ctx, t, logger)
base := time.Date(2024, 3, 1, 0, 0, 0, 0, time.UTC)
events := []auditlog.Event{
{
Type: "create",
ResourceType: "omni.Cluster",
ResourceID: "cluster-a",
TimeMillis: base.Add(1 * time.Second).UnixMilli(),
Data: &auditlog.Data{
Session: auditlog.Session{Email: "alice@example.com"},
Cluster: &auditlog.Cluster{ID: "cluster-a"},
},
},
{
Type: "update",
ResourceType: "omni.Cluster",
ResourceID: "cluster-b",
TimeMillis: base.Add(2 * time.Second).UnixMilli(),
Data: &auditlog.Data{
Session: auditlog.Session{Email: "bob@example.com"},
Cluster: &auditlog.Cluster{ID: "cluster-b"},
},
},
{
Type: "create",
ResourceType: "omni.MachineSetNode",
ResourceID: "node-1",
TimeMillis: base.Add(3 * time.Second).UnixMilli(),
Data: &auditlog.Data{
Session: auditlog.Session{Email: "alice@example.com"},
MachineSetNode: &auditlog.MachineSetNode{ID: "node-1", ClusterID: "cluster-a"},
},
},
}
for _, evt := range events {
require.NoError(t, store.Write(ctx, evt))
}
timeFilters := auditlog.ReadFilters{
Start: base,
End: base.Add(1 * time.Minute),
}
tests := []struct {
name string
filters auditlog.ReadFilters
wantCount int
}{
{
name: "EventType=create",
filters: auditlog.ReadFilters{EventType: auditlog.EventTypeCreate},
wantCount: 2,
},
{
name: "EventType=update",
filters: auditlog.ReadFilters{EventType: auditlog.EventTypeUpdate},
wantCount: 1,
},
{
name: "ResourceType=omni.Cluster",
filters: auditlog.ReadFilters{ResourceType: "omni.Cluster"},
wantCount: 2,
},
{
name: "ResourceType=omni.MachineSetNode",
filters: auditlog.ReadFilters{ResourceType: "omni.MachineSetNode"},
wantCount: 1,
},
{
name: "ResourceID=cluster-a",
filters: auditlog.ReadFilters{ResourceID: "cluster-a"},
wantCount: 1,
},
{
name: "ClusterID=cluster-a",
filters: auditlog.ReadFilters{ClusterID: "cluster-a"},
wantCount: 2,
},
{
name: "Actor=alice@example.com",
filters: auditlog.ReadFilters{Actor: "alice@example.com"},
wantCount: 2,
},
{
name: "Actor=bob@example.com",
filters: auditlog.ReadFilters{Actor: "bob@example.com"},
wantCount: 1,
},
{
name: "Actor=alice + EventType=update (no match)",
filters: auditlog.ReadFilters{Actor: "alice@example.com", EventType: auditlog.EventTypeUpdate},
wantCount: 0,
},
{
name: "Actor=alice + EventType=create",
filters: auditlog.ReadFilters{Actor: "alice@example.com", EventType: auditlog.EventTypeCreate},
wantCount: 2,
},
{
name: "Search=alice (matches actor_email)",
filters: auditlog.ReadFilters{Search: "alice"},
wantCount: 2,
},
{
name: "Search=cluster-b (matches resource_id and cluster_id)",
filters: auditlog.ReadFilters{Search: "cluster-b"},
wantCount: 1,
},
{
name: "Search=omni.Cluster (matches resource_type)",
filters: auditlog.ReadFilters{Search: "omni.Cluster"},
wantCount: 2,
},
{
name: "Search=update (matches event_type)",
filters: auditlog.ReadFilters{Search: "update"},
wantCount: 1,
},
{
name: "Search=node-1 (matches resource_id in event_data JSON)",
filters: auditlog.ReadFilters{Search: "node-1"},
wantCount: 1,
},
{
name: "Search=no-match",
filters: auditlog.ReadFilters{Search: "zzz-no-match-zzz"},
wantCount: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
f := tt.filters
f.Start = timeFilters.Start
f.End = timeFilters.End
rdr, err := store.Reader(ctx, f)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, rdr.Close())
})
got := readAllEvents(t, rdr)
assert.Len(t, got, tt.wantCount)
})
}
}
func setupStore(ctx context.Context, t *testing.T, logger *zap.Logger) (*auditlogsqlite.Store, *sqlitexx.Pool) {
return setupStoreWithOpts(ctx, t, logger, 0, 0)
}

View File

@ -44,7 +44,7 @@ func (n *nopLogger) Write(context.Context, auditlog.Event) error { return nil }
func (n *nopLogger) Remove(context.Context, time.Time, time.Time) error { return nil }
func (n *nopLogger) Reader(context.Context, time.Time, time.Time) (auditlog.Reader, error) {
func (n *nopLogger) Reader(context.Context, auditlog.ReadFilters) (auditlog.Reader, error) {
return &nopReader{}, nil
}

View File

@ -359,12 +359,12 @@ type AuditWrap struct {
}
// Reader reads the audit log file by file, oldest to newest.
func (w *AuditWrap) Reader(ctx context.Context, start, end time.Time) (auditlog.Reader, error) {
func (w *AuditWrap) Reader(ctx context.Context, filters auditlog.ReadFilters) (auditlog.Reader, error) {
if w.log == nil {
return nil, errors.New("audit log is disabled")
}
return w.log.Reader(ctx, start, end)
return w.log.Reader(ctx, filters)
}
// RunCleanup runs wrapped [audit.Log.RunCleanup] if the audit log is enabled. Otherwise, blocks until context is

View File

@ -710,7 +710,7 @@ func AssertAPIAuthz(rootCtx context.Context, rootCli *client.Client, clientFacto
assertSuccess: assertSuccess,
assertFailure: assertMissingRoleFailure,
fn: func(ctx context.Context, cli *client.Client) error {
for _, err := range cli.Management().ReadAuditLog(ctx, "", "") {
for _, err := range cli.Management().ReadAuditLog(ctx, nil) {
if err != nil {
return err
}