Add audit event for Classifier delete, docs, and web UI support (#67763)

* Add audit event for Classifier delete, docs, and web UI support

* Add failure code for the Classifier delete audit event

* Add web UI and docs support for Classifier audit failure codes
This commit is contained in:
Ryan Clark
2026-06-16 20:44:52 +00:00
committed by GitHub
parent 9691b677d4
commit 6aaa43440e
13 changed files with 2211 additions and 1531 deletions
@@ -5203,6 +5203,7 @@ message OneOf {
events.BeamsConfigDelete BeamsConfigDelete = 259;
events.ClassifierCreate ClassifierCreate = 260;
events.ClassifierUpdate ClassifierUpdate = 261;
events.ClassifierDelete ClassifierDelete = 262;
}
}
@@ -10827,3 +10828,41 @@ message ClassifierUpdate {
(gogoproto.casttype) = "Struct"
];
}
// ClassifierDelete is emitted when a classifier is deleted.
message ClassifierDelete {
// Metadata is a common event metadata
Metadata Metadata = 1 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// ResourceMetadata is a common resource event metadata
ResourceMetadata Resource = 2 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// User is a common user event metadata
UserMetadata User = 3 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// ConnectionMetadata holds information about the connection
ConnectionMetadata Connection = 4 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
// Status indicates whether the deletion was successful.
Status Status = 5 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];
}
+4
View File
@@ -3046,3 +3046,7 @@ func (m *ClassifierCreate) TrimToMaxSize(_ int) AuditEvent {
func (m *ClassifierUpdate) TrimToMaxSize(_ int) AuditEvent {
return m
}
func (m *ClassifierDelete) TrimToMaxSize(_ int) AuditEvent {
return m
}
+1982 -1531
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -1064,6 +1064,10 @@ func ToOneOf(in AuditEvent) (*OneOf, error) {
out.Event = &OneOf_ClassifierUpdate{
ClassifierUpdate: e,
}
case *ClassifierDelete:
out.Event = &OneOf_ClassifierDelete{
ClassifierDelete: e,
}
default:
slog.ErrorContext(context.Background(), "Attempted to convert dynamic event of unknown type into protobuf event.", "event_type", in.GetType())
+57
View File
@@ -1743,6 +1743,63 @@ Example:
}
```
## classifier.delete
There are multiple events with the `classifier.delete` type.
### INF016I
Classifier Deleted
Example:
```json
{
"addr.remote": "127.0.0.1:62460",
"cluster_name": "teleport.dev",
"code": "INF016I",
"ei": 0,
"event": "classifier.delete",
"name": "my-classifier",
"success": true,
"time": "2025-03-04T16:03:21.869Z",
"uid": "b0973df2-4bee-4a67-9925-575c1d38cc80",
"user": "08fcd522-edcb-492d-8752-90494a28b70e.teleport.dev",
"user_cluster_name": "teleport.dev",
"user_kind": 3,
"user_roles": [
"Admin"
]
}
```
### INF016E
Classifier Deletion Failed
Example:
```json
{
"addr.remote": "127.0.0.1:62460",
"cluster_name": "teleport.dev",
"code": "INF016E",
"ei": 0,
"event": "classifier.delete",
"name": "my-classifier",
"error": "classifier \"my-classifier\" doesn't exist",
"success": false,
"time": "2025-03-04T16:03:31.869Z",
"uid": "b3973df2-4bee-4a67-9925-575c1d38cc80",
"user": "08fcd522-edcb-492d-8752-90494a28b70e.teleport.dev",
"user_cluster_name": "teleport.dev",
"user_kind": 3,
"user_roles": [
"Admin"
]
}
```
## classifier.update
There are multiple events with the `classifier.update` type.
+2
View File
@@ -1042,6 +1042,8 @@ const (
ClassifierCreateEvent = "classifier.create"
// ClassifierUpdateEvent is emitted when a classifier resource is updated.
ClassifierUpdateEvent = "classifier.update"
// ClassifierDeleteEvent is emitted when a classifier resource is deleted.
ClassifierDeleteEvent = "classifier.delete"
// SessionSummarizedEvent is emitted when a session summary is created.
SessionSummarizedEvent = "session.summarized"
+4
View File
@@ -906,6 +906,10 @@ const (
ClassifierUpdateCode = "INF015I"
// ClassifierUpdateFailureCode is the classifier update failure event code.
ClassifierUpdateFailureCode = "INF015E"
// ClassifierDeleteCode is the classifier delete event code.
ClassifierDeleteCode = "INF016I"
// ClassifierDeleteFailureCode is the classifier delete failure event code.
ClassifierDeleteFailureCode = "INF016E"
// CertAuthOverrideCreateCode is the cert_auth_override create event code.
CertAuthOverrideCreateCode = "TCO01I"
+2
View File
@@ -638,6 +638,8 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) {
e = &events.ClassifierCreate{}
case ClassifierUpdateEvent:
e = &events.ClassifierUpdate{}
case ClassifierDeleteEvent:
e = &events.ClassifierDelete{}
case SessionSummarizedEvent:
e = &events.SessionSummarized{}
+63
View File
@@ -291,6 +291,9 @@ var eventsMap = map[string]apievents.AuditEvent{
RetrievalModelCreateEvent: &apievents.RetrievalModelCreate{},
RetrievalModelUpdateEvent: &apievents.RetrievalModelUpdate{},
RetrievalModelDeleteEvent: &apievents.RetrievalModelDelete{},
ClassifierCreateEvent: &apievents.ClassifierCreate{},
ClassifierUpdateEvent: &apievents.ClassifierUpdate{},
ClassifierDeleteEvent: &apievents.ClassifierDelete{},
SessionSummarizedEvent: &apievents.SessionSummarized{},
SCIMListingEvent: &apievents.SCIMListingEvent{},
SCIMGetEvent: &apievents.SCIMResourceEvent{},
@@ -1749,6 +1752,66 @@ func TestInferenceEvents(t *testing.T) {
eventCode: BeamsConfigDeleteCode,
hasPayload: false,
},
{
name: "ClassifierCreate",
event: &apievents.ClassifierCreate{
Metadata: apievents.Metadata{
Type: ClassifierCreateEvent,
Code: ClassifierCreateCode,
Time: testTime,
ClusterName: "test-cluster",
},
ResourceMetadata: apievents.ResourceMetadata{
Name: "test-classifier",
},
UserMetadata: apievents.UserMetadata{
User: "test-user",
},
},
eventType: ClassifierCreateEvent,
eventCode: ClassifierCreateCode,
hasPayload: true,
},
{
name: "ClassifierUpdate",
event: &apievents.ClassifierUpdate{
Metadata: apievents.Metadata{
Type: ClassifierUpdateEvent,
Code: ClassifierUpdateCode,
Time: testTime,
ClusterName: "test-cluster",
},
ResourceMetadata: apievents.ResourceMetadata{
Name: "test-classifier",
},
UserMetadata: apievents.UserMetadata{
User: "test-user",
},
},
eventType: ClassifierUpdateEvent,
eventCode: ClassifierUpdateCode,
hasPayload: true,
},
{
name: "ClassifierDelete",
event: &apievents.ClassifierDelete{
Metadata: apievents.Metadata{
Type: ClassifierDeleteEvent,
Code: ClassifierDeleteCode,
Time: testTime,
ClusterName: "test-cluster",
},
ResourceMetadata: apievents.ResourceMetadata{
Name: "test-classifier",
},
UserMetadata: apievents.UserMetadata{
User: "test-user",
},
},
eventType: ClassifierDeleteEvent,
eventCode: ClassifierDeleteCode,
hasPayload: false,
},
}
for _, tt := range tests {
@@ -377,6 +377,8 @@ export const EventIconMap: Record<EventCode, any> = {
[eventCodes.CLASSIFIER_CREATE_FAILURE]: Icons.Warning,
[eventCodes.CLASSIFIER_UPDATE]: Icons.Info,
[eventCodes.CLASSIFIER_UPDATE_FAILURE]: Icons.Warning,
[eventCodes.CLASSIFIER_DELETE]: Icons.Info,
[eventCodes.CLASSIFIER_DELETE_FAILURE]: Icons.Warning,
[eventCodes.SESSION_SUMMARIZED]: Icons.Info,
[eventCodes.SESSION_SUMMARIZED_FAILURE]: Icons.Warning,
[eventCodes.CERT_AUTH_OVERRIDE_CREATE]: Icons.Info,
@@ -4983,6 +4983,37 @@ export const events = [
user_kind: 3,
user_roles: ['Admin'],
},
{
'addr.remote': '127.0.0.1:62460',
cluster_name: 'teleport.dev',
code: 'INF016I',
ei: 0,
event: 'classifier.delete',
name: 'my-classifier',
success: true,
time: '2025-03-04T16:03:21.869Z',
uid: 'b0973df2-4bee-4a67-9925-575c1d38cc80',
user: '08fcd522-edcb-492d-8752-90494a28b70e.teleport.dev',
user_cluster_name: 'teleport.dev',
user_kind: 3,
user_roles: ['Admin'],
},
{
'addr.remote': '127.0.0.1:62460',
cluster_name: 'teleport.dev',
code: 'INF016E',
ei: 0,
event: 'classifier.delete',
name: 'my-classifier',
error: 'classifier "my-classifier" doesn\'t exist',
success: false,
time: '2025-03-04T16:03:31.869Z',
uid: 'b3973df2-4bee-4a67-9925-575c1d38cc80',
user: '08fcd522-edcb-492d-8752-90494a28b70e.teleport.dev',
user_cluster_name: 'teleport.dev',
user_kind: 3,
user_roles: ['Admin'],
},
{
cluster_name: 'teleport.dev',
code: 'INF010I',
@@ -2669,6 +2669,17 @@ export const formatters: Formatters = {
format: ({ name, user }) =>
`Classifier [${name}] failed to be updated by [${user}]`,
},
[eventCodes.CLASSIFIER_DELETE]: {
type: 'classifier.delete',
desc: 'Classifier Deleted',
format: ({ name, user }) => `Classifier [${name}] was deleted by [${user}]`,
},
[eventCodes.CLASSIFIER_DELETE_FAILURE]: {
type: 'classifier.delete',
desc: 'Classifier Deletion Failed',
format: ({ name, user }) =>
`Classifier [${name}] failed to be deleted by [${user}]`,
},
[eventCodes.SESSION_SUMMARIZED]: {
type: 'session.summarized',
desc: 'Session Summarized',
@@ -401,6 +401,8 @@ export const eventCodes = {
CLASSIFIER_CREATE_FAILURE: 'INF014E',
CLASSIFIER_UPDATE: 'INF015I',
CLASSIFIER_UPDATE_FAILURE: 'INF015E',
CLASSIFIER_DELETE: 'INF016I',
CLASSIFIER_DELETE_FAILURE: 'INF016E',
CERT_AUTH_OVERRIDE_CREATE: 'TCO01I',
CERT_AUTH_OVERRIDE_UPDATE: 'TCO02I',
CERT_AUTH_OVERRIDE_UPSERT: 'TCO03I',
@@ -2354,6 +2356,14 @@ export type RawEvents = {
typeof eventCodes.CLASSIFIER_UPDATE_FAILURE,
HasName
>;
[eventCodes.CLASSIFIER_DELETE]: RawEvent<
typeof eventCodes.CLASSIFIER_DELETE,
HasName
>;
[eventCodes.CLASSIFIER_DELETE_FAILURE]: RawEvent<
typeof eventCodes.CLASSIFIER_DELETE_FAILURE,
HasName
>;
[eventCodes.SESSION_SUMMARIZED]: RawEvent<
typeof eventCodes.SESSION_SUMMARIZED,
{