From e5e131845ec5c9bb749cb6638262f46568c71b15 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Fri, 21 Feb 2025 18:18:51 +0800 Subject: [PATCH] Add OIDC login event (#21650) Signed-off-by: stonezdj --- src/core/controllers/oidc.go | 17 +++++++++++++++++ src/pkg/auditext/event/login/login.go | 10 ++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/core/controllers/oidc.go b/src/core/controllers/oidc.go index 295acb2cf8b..606c7526575 100644 --- a/src/core/controllers/oidc.go +++ b/src/core/controllers/oidc.go @@ -24,11 +24,13 @@ import ( "github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/utils" + "github.com/goharbor/harbor/src/controller/event/metadata/commonevent" ctluser "github.com/goharbor/harbor/src/controller/user" "github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/pkg/notification" "github.com/goharbor/harbor/src/pkg/oidc" ) @@ -38,6 +40,8 @@ const userInfoKey = "oidc_user_info" const redirectURLKey = "oidc_redirect_url" const oidcUserComment = "Onboarded via OIDC provider" +const loginUserOperation = "login_user" + // OIDCController handles requests for OIDC login, callback and user onboard type OIDCController struct { api.BaseController @@ -208,6 +212,19 @@ func (oc *OIDCController) Callback() { redirectURLStr = "/" } oc.Controller.Redirect(redirectURLStr, http.StatusFound) + // The log middleware can capture the OIDC user login event with the URL, but it cannot get the current username from security context because the security context is not ready yet. + // need to create login event in the OIDC login call back logic + // to avoid generate duplicate event in audit log ext, the PreCheck function of the login event intentionally bypass the OIDC user login event in log middleware + // and OIDC's login callback function will create the login event and send it to notification. + if config.AuditLogEventEnabled(ctx, loginUserOperation) { + e := &commonevent.Metadata{ + Ctx: ctx, + Username: u.Username, + RequestMethod: oc.Ctx.Request.Method, + RequestURL: oc.Ctx.Request.URL.String(), + } + notification.AddEvent(e.Ctx, e, true) + } } func userOnboard(ctx context.Context, oc *OIDCController, info *oidc.UserInfo, username string, tokenBytes []byte) (*models.User, bool) { diff --git a/src/pkg/auditext/event/login/login.go b/src/pkg/auditext/event/login/login.go index 00e8dfa4c5e..4c340a026fe 100644 --- a/src/pkg/auditext/event/login/login.go +++ b/src/pkg/auditext/event/login/login.go @@ -33,6 +33,7 @@ func init() { var login = &loginResolver{} var logout = &logoutResolver{} commonevent.RegisterResolver(`/c/login$`, login) + commonevent.RegisterResolver(`/c/oidc/callback.*`, login) commonevent.RegisterResolver(`/c/log_out$`, logout) } @@ -54,7 +55,7 @@ func (l *loginResolver) Resolve(ce *commonevent.Metadata, event *event.Event) er OcurrAt: time.Now(), Operation: opLogin, OperationDescription: opLogin, - IsSuccessful: true, + IsSuccessful: ce.ResponseCode <= http.StatusTemporaryRedirect, } // Extract the username from payload @@ -65,9 +66,10 @@ func (l *loginResolver) Resolve(ce *commonevent.Metadata, event *event.Event) er e.ResourceName = match[1] e.Operator = match[1] } - } - if ce.ResponseCode != http.StatusOK { - e.IsSuccessful = false + } else if ce.RequestMethod == http.MethodGet { + e.IsSuccessful = true // for OIDC login event, always success + e.Operator = ce.Username + e.ResourceName = ce.Username } event.Topic = ctlevent.TopicCommonEvent event.Data = e