diff --git a/src/common/const.go b/src/common/const.go index 730dac0b2..c77d75469 100644 --- a/src/common/const.go +++ b/src/common/const.go @@ -139,6 +139,7 @@ const ( RobotTokenDuration = "robot_token_duration" OIDCCallbackPath = "/c/oidc/callback" + OIDCLoginPath = "/c/oidc/login" ChartUploadCtxKey = contextKey("chart_upload_event") ) diff --git a/src/core/controllers/base.go b/src/core/controllers/base.go index 88c9a7ba5..0c82746a3 100644 --- a/src/core/controllers/base.go +++ b/src/core/controllers/base.go @@ -16,12 +16,15 @@ package controllers import ( "bytes" + "context" + "github.com/goharbor/harbor/src/core/filter" "html/template" "net" "net/http" "os" "regexp" "strconv" + "strings" "github.com/astaxie/beego" "github.com/beego/i18n" @@ -54,10 +57,43 @@ type messageDetail struct { UUID string } +func redirectForOIDC(ctx context.Context, username string) bool { + am, _ := ctx.Value(filter.AuthModeKey).(string) + if am != common.OIDCAuth { + return false + } + u, err := dao.GetUser(models.User{Username: username}) + if err != nil { + log.Warningf("Failed to get user by name: %s, error: %v", username, err) + } + if u == nil { + return true + } + ou, err := dao.GetOIDCUserByUserID(u.UserID) + if err != nil { + log.Warningf("Failed to get OIDC user info for user, id: %d, error: %v", u.UserID, err) + } + if ou != nil { + return true + } + return false +} + // Login handles login request from UI. func (cc *CommonController) Login() { principal := cc.GetString("principal") password := cc.GetString("password") + if redirectForOIDC(cc.Ctx.Request.Context(), principal) { + ep, err := config.ExtEndpoint() + if err != nil { + log.Errorf("Failed to get the external endpoint, error: %v", err) + cc.CustomAbort(http.StatusUnauthorized, "") + } + url := strings.TrimSuffix(ep, "/") + common.OIDCLoginPath + log.Debugf("Redirect user %s to login page of OIDC provider", principal) + cc.Redirect(url, http.StatusFound) + return + } user, err := auth.Login(models.AuthModel{ Principal: principal, diff --git a/src/core/controllers/controllers_test.go b/src/core/controllers/controllers_test.go index 49d6e00d6..34358e6a5 100644 --- a/src/core/controllers/controllers_test.go +++ b/src/core/controllers/controllers_test.go @@ -14,6 +14,8 @@ package controllers import ( + "context" + "github.com/goharbor/harbor/src/core/filter" "net/http" "net/http/httptest" // "net/url" @@ -90,6 +92,15 @@ func TestUserResettable(t *testing.T) { assert.True(isUserResetable(u1)) } +func TestRedirectForOIDC(t *testing.T) { + ctx := context.WithValue(context.Background(), filter.AuthModeKey, common.DBAuth) + assert.False(t, redirectForOIDC(ctx, "nonexist")) + ctx = context.WithValue(context.Background(), filter.AuthModeKey, common.OIDCAuth) + assert.True(t, redirectForOIDC(ctx, "nonexist")) + assert.False(t, redirectForOIDC(ctx, "admin")) + +} + // TestMain is a sample to run an endpoint test func TestAll(t *testing.T) { config.InitWithSettings(utilstest.GetUnitTestConfig()) diff --git a/src/core/router.go b/src/core/router.go index d1beb12f6..1c4c31f3f 100644 --- a/src/core/router.go +++ b/src/core/router.go @@ -38,7 +38,7 @@ func initRouters() { beego.Router("/c/reset", &controllers.CommonController{}, "post:ResetPassword") beego.Router("/c/userExists", &controllers.CommonController{}, "post:UserExists") beego.Router("/c/sendEmail", &controllers.CommonController{}, "get:SendResetEmail") - beego.Router("/c/oidc/login", &controllers.OIDCController{}, "get:RedirectLogin") + beego.Router(common.OIDCLoginPath, &controllers.OIDCController{}, "get:RedirectLogin") beego.Router("/c/oidc/onboard", &controllers.OIDCController{}, "post:Onboard") beego.Router(common.OIDCCallbackPath, &controllers.OIDCController{}, "get:Callback")