Fix issue with user create/delete/update event (#21651)

Signed-off-by: stonezdj <stone.zhang@broadcom.com>
This commit is contained in:
stonezdj(Daojun Zhang) 2025-02-25 11:42:48 +08:00 committed by GitHub
parent e5e131845e
commit 4cd06777c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 27 additions and 27 deletions

View File

@ -186,7 +186,7 @@ func TestPermitEventTypes(t *testing.T) {
}
// test other event types
otherEventTypes := permitEventTypes([]string{"create_artifact", "delete_artifact", "pull_artifact", "other_events"})
otherEventTypes := permitEventTypes([]string{"create_artifact", "delete_artifact", "pull_artifact", "other"})
if len(otherEventTypes) != len(model.EventTypes) {
t.Errorf("permitOtherEventTypes failed, it should include all event types")
}

View File

@ -31,25 +31,25 @@ import (
)
const (
createOp = "create"
updateOp = "update"
deleteOp = "delete"
resourceIDPattern = `^%v/(\d+)$`
createOp = "create"
updateOp = "update"
deleteOp = "delete"
)
// ResolveIDToNameFunc is the function to resolve the resource name from resource id
type ResolveIDToNameFunc func(string) string
type Resolver struct {
BaseURLPattern string
ResourceType string
SucceedCodes []int
ResourceType string
SucceedCodes []int
// SensitiveAttributes is the attributes that need to be redacted
SensitiveAttributes []string
// ShouldResolveName indicates if the resource name should be resolved before delete, if true, need to resolve the resource name before delete
ShouldResolveName bool
// IDToNameFunc is used to resolve the resource name from resource id
IDToNameFunc ResolveIDToNameFunc
// ResourceIDPattern the URL pattern to match the resourceID
ResourceIDPattern string
}
// PreCheck check if the event should be captured and resolve the resource name if needed, if need to resolve the resource name, return the resource name
@ -61,9 +61,9 @@ func (e *Resolver) PreCheck(ctx context.Context, url string, method string) (cap
// for delete operation on a resource has name, need to resolve the resource id to resource name before delete
resName := ""
if capture && method == http.MethodDelete && e.ShouldResolveName {
re := regexp.MustCompile(fmt.Sprintf(resourceIDPattern, e.BaseURLPattern))
re := regexp.MustCompile(e.ResourceIDPattern)
m := re.FindStringSubmatch(url)
if len(m) == 2 && e.IDToNameFunc != nil {
if len(m) >= 2 && e.IDToNameFunc != nil {
resName = e.IDToNameFunc(m[1])
}
}
@ -92,9 +92,9 @@ func (e *Resolver) Resolve(ce *commonevent.Metadata, event *event.Event) error {
case createOp:
if len(ce.ResponseLocation) > 0 {
// extract resource id from response location
re := regexp.MustCompile(fmt.Sprintf(resourceIDPattern, e.BaseURLPattern))
re := regexp.MustCompile(e.ResourceIDPattern)
m := re.FindStringSubmatch(ce.ResponseLocation)
if len(m) != 2 {
if len(m) < 2 {
return nil
}
evt.ResourceName = m[1]
@ -107,9 +107,9 @@ func (e *Resolver) Resolve(ce *commonevent.Metadata, event *event.Event) error {
}
case deleteOp:
re := regexp.MustCompile(fmt.Sprintf(resourceIDPattern, e.BaseURLPattern))
re := regexp.MustCompile(e.ResourceIDPattern)
m := re.FindStringSubmatch(ce.RequestURL)
if len(m) != 2 {
if len(m) < 2 {
return nil
}
evt.ResourceName = m[1]
@ -118,9 +118,9 @@ func (e *Resolver) Resolve(ce *commonevent.Metadata, event *event.Event) error {
}
case updateOp:
re := regexp.MustCompile(fmt.Sprintf(resourceIDPattern, e.BaseURLPattern))
re := regexp.MustCompile(e.ResourceIDPattern)
m := re.FindStringSubmatch(ce.RequestURL)
if len(m) != 2 {
if len(m) < 2 {
return nil
}
evt.ResourceName = m[1]

View File

@ -21,12 +21,12 @@ import (
func TestEventResolver_PreCheck(t *testing.T) {
type fields struct {
BaseURLPattern string
ResourceType string
SucceedCodes []int
SensitiveAttributes []string
ShouldResolveName bool
IDToNameFunc ResolveIDToNameFunc
ResourceIDPattern string
}
type args struct {
ctx context.Context
@ -40,14 +40,14 @@ func TestEventResolver_PreCheck(t *testing.T) {
wantCapture bool
wantResourceName string
}{
{"test normal", fields{BaseURLPattern: `/api/v2.0/tests`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true, IDToNameFunc: func(string) string { return "test" }}, args{context.Background(), "/api/v2.0/tests/123", "DELETE"}, true, "test"},
{"test resource name", fields{BaseURLPattern: `/api/v2.0/tests`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true, IDToNameFunc: func(string) string { return "test_resource_name" }}, args{context.Background(), "/api/v2.0/tests/234", "DELETE"}, true, "test_resource_name"},
{"test no resource name", fields{BaseURLPattern: `/api/v2.0/tests`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true}, args{context.Background(), "/api/v2.0/tests/234", "GET"}, true, ""},
{"test normal", fields{ResourceIDPattern: `/api/v2.0/tests/(\d+)`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true, IDToNameFunc: func(string) string { return "test" }}, args{context.Background(), "/api/v2.0/tests/123", "DELETE"}, true, "test"},
{"test resource name", fields{ResourceIDPattern: `/api/v2.0/tests/(\d+)`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true, IDToNameFunc: func(string) string { return "test_resource_name" }}, args{context.Background(), "/api/v2.0/tests/234", "DELETE"}, true, "test_resource_name"},
{"test no resource name", fields{ResourceIDPattern: `/api/v2.0/tests/(\d+)`, ResourceType: "test", SucceedCodes: []int{200}, ShouldResolveName: true}, args{context.Background(), "/api/v2.0/tests/234", "GET"}, true, ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &Resolver{
BaseURLPattern: tt.fields.BaseURLPattern,
ResourceIDPattern: tt.fields.ResourceIDPattern,
ResourceType: tt.fields.ResourceType,
SucceedCodes: tt.fields.SucceedCodes,
SensitiveAttributes: tt.fields.SensitiveAttributes,

View File

@ -26,21 +26,21 @@ import (
pkgUser "github.com/goharbor/harbor/src/pkg/user"
)
const urlPattern = `^/api/v2.0/users/(\d+)(/password|/sysadmin)?$`
func init() {
var userResolver = &userEventResolver{
Resolver: event.Resolver{
BaseURLPattern: "/api/v2.0/users",
ResourceType: rbac.ResourceUser.String(),
SucceedCodes: []int{http.StatusCreated, http.StatusOK},
SensitiveAttributes: []string{"password"},
ShouldResolveName: true,
IDToNameFunc: userIDToName,
ResourceIDPattern: urlPattern,
},
}
commonevent.RegisterResolver(`/api/v2.0/users$`, userResolver)
commonevent.RegisterResolver(`^/api/v2.0/users/\d+/password$`, userResolver)
commonevent.RegisterResolver(`^/api/v2.0/users/\d+/sysadmin$`, userResolver)
commonevent.RegisterResolver(`^/api/v2.0/users/\d+$`, userResolver)
commonevent.RegisterResolver(urlPattern, userResolver)
}
type userEventResolver struct {

View File

@ -20,7 +20,7 @@ import (
beego_orm "github.com/beego/beego/v2/client/orm"
)
const OtherEvents = "other_events"
const OtherEvents = "other"
func init() {
beego_orm.RegisterModel(&AuditLogExt{})
@ -60,7 +60,7 @@ var EventTypes = []string{
"update_user",
"create_robot",
"delete_robot",
"update_configure",
"update_configuration",
}
// OtherEventTypes defines the types of other audit log event types excludes previous EventTypes: create_artifact, delete_artifact, pull_artifact