From 6d0e391740bb304650421c530f91a68e6f26bde7 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Fri, 7 May 2021 16:07:19 +0800 Subject: [PATCH] Move user related funcs from common/dao This commit moves more user related funcs, such as ChangePassword, Login, ChangeUserProfile from common/dao to rely on /pkg/user and pkg/oidc. It also removes the code for resetting user's password as it's disabled. Signed-off-by: Daniel Jiang --- src/common/dao/dao_test.go | 123 ----------------- src/common/dao/oidc_user.go | 38 ------ src/common/dao/oidc_user_test.go | 5 - src/common/dao/user.go | 104 -------------- src/common/models/user.go | 20 +++ src/common/utils/test/database.go | 7 +- src/controller/user/controller.go | 26 +++- src/core/api/internal.go | 8 +- src/core/auth/authenticator.go | 28 ++-- src/core/auth/db/db.go | 4 +- src/core/auth/ldap/ldap.go | 2 +- src/core/auth/uaa/uaa.go | 8 +- src/core/controllers/base.go | 157 +--------------------- src/core/controllers/controllers_test.go | 39 +----- src/core/controllers/oidc.go | 38 +++--- src/core/main.go | 15 +-- src/pkg/oidc/metamanager.go | 17 +++ src/pkg/user/dao/dao_test.go | 30 ++++- src/pkg/user/manager.go | 32 +++-- src/server/middleware/security/idtoken.go | 10 +- src/testing/controller/user/controller.go | 50 +++++-- src/testing/pkg/user/manager.go | 61 +++++---- 22 files changed, 252 insertions(+), 570 deletions(-) diff --git a/src/common/dao/dao_test.go b/src/common/dao/dao_test.go index 4f8689080..458f40e12 100644 --- a/src/common/dao/dao_test.go +++ b/src/common/dao/dao_test.go @@ -23,7 +23,6 @@ import ( "github.com/astaxie/beego/orm" "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/lib/log" libOrm "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/pkg/user" @@ -154,45 +153,6 @@ func clearAll() { } } -func TestLoginByUserName(t *testing.T) { - loginUser, err := LoginByDb(models.AuthModel{ - Principal: username, - Password: password, - }) - if err != nil { - t.Errorf("Error occurred in LoginByDb: %v", err) - } - if loginUser == nil { - t.Errorf("No found for user logined by username and password: %s, %s", username, password) - } - - if loginUser.Username != username { - t.Errorf("User's username does not match after login, expected: %s, actual: %s", username, loginUser.Username) - } -} - -func TestLoginByEmail(t *testing.T) { - - userQuery := models.User{ - Email: "tester01@vmware.com", - Password: "Abc12345", - } - - loginUser, err := LoginByDb(models.AuthModel{ - Principal: userQuery.Email, - Password: userQuery.Password, - }) - if err != nil { - t.Errorf("Error occurred in LoginByDb: %v", err) - } - if loginUser == nil { - t.Errorf("No found for user logined by email and password : %v", userQuery) - } - if loginUser.Username != username { - t.Errorf("User's username does not match after login, expected: %s, actual: %s", username, loginUser.Username) - } -} - var currentUser *models.User func TestGetUser(t *testing.T) { @@ -217,60 +177,6 @@ func TestGetUser(t *testing.T) { assert.NotNil(t, err) } -func TestResetUserPassword(t *testing.T) { - uuid := utils.GenerateRandomString() - - err := UpdateUserResetUUID(models.User{ResetUUID: uuid, Email: currentUser.Email}) - if err != nil { - t.Errorf("Error occurred in UpdateUserResetUuid: %v", err) - } - - err = ResetUserPassword( - models.User{ - UserID: currentUser.UserID, - PasswordVersion: utils.SHA256, - ResetUUID: uuid, - Salt: currentUser.Salt}, "HarborTester12345") - if err != nil { - t.Errorf("Error occurred in ResetUserPassword: %v", err) - } - - loginedUser, err := LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "HarborTester12345"}) - if err != nil { - t.Errorf("Error occurred in LoginByDb: %v", err) - } - - if loginedUser.Username != username { - t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", username, loginedUser.Username) - } -} - -func TestChangeUserPassword(t *testing.T) { - user := models.User{UserID: currentUser.UserID} - query, err := GetUser(user) - if err != nil { - t.Errorf("Error occurred when get user salt") - } - currentUser.Salt = query.Salt - err = ChangeUserPassword( - models.User{ - UserID: currentUser.UserID, - Password: "NewHarborTester12345", - PasswordVersion: utils.SHA256, - Salt: currentUser.Salt}) - if err != nil { - t.Errorf("Error occurred in ChangeUserPassword: %v", err) - } - - loginedUser, err := LoginByDb(models.AuthModel{Principal: currentUser.Username, Password: "NewHarborTester12345"}) - if err != nil { - t.Errorf("Error occurred in LoginByDb: %v", err) - } - - if loginedUser.Username != username { - t.Errorf("The username returned by Login does not match, expected: %s, acutal: %s", username, loginedUser.Username) - } -} func TestAddProject(t *testing.T) { project := models.Project{ @@ -345,29 +251,6 @@ func TestGetProjects(t *testing.T) { } } -func TestChangeUserProfile(t *testing.T) { - user := models.User{UserID: currentUser.UserID, Email: username + "@163.com", Realname: "test", Comment: "Unit Test"} - err := ChangeUserProfile(user) - if err != nil { - t.Errorf("Error occurred in ChangeUserProfile: %v", err) - } - loginedUser, err := GetUser(models.User{UserID: currentUser.UserID}) - if err != nil { - t.Errorf("Error occurred in GetUser: %v", err) - } - if loginedUser != nil { - if loginedUser.Email != username+"@163.com" { - t.Errorf("user email does not update, expected: %s, acutal: %s", username+"@163.com", loginedUser.Email) - } - if loginedUser.Realname != "test" { - t.Errorf("user realname does not update, expected: %s, acutal: %s", "test", loginedUser.Realname) - } - if loginedUser.Comment != "Unit Test" { - t.Errorf("user email does not update, expected: %s, acutal: %s", "Unit Test", loginedUser.Comment) - } - } -} - var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64 func TestGetOrmer(t *testing.T) { @@ -430,12 +313,6 @@ func TestDeleteRepository(t *testing.T) { } } -func TestIsSuperUser(t *testing.T) { - assert := assert.New(t) - assert.True(IsSuperUser("admin")) - assert.False(IsSuperUser("none")) -} - func TestIsDupRecError(t *testing.T) { assert.True(t, IsDupRecErr(fmt.Errorf("pq: duplicate key value violates unique constraint \"properties_k_key\""))) assert.False(t, IsDupRecErr(fmt.Errorf("other error"))) diff --git a/src/common/dao/oidc_user.go b/src/common/dao/oidc_user.go index 4d6f69823..fb8c0c2a9 100644 --- a/src/common/dao/oidc_user.go +++ b/src/common/dao/oidc_user.go @@ -39,44 +39,6 @@ var ( ErrRollBackOIDCUser = errors.New("sql: transaction roll back error in oicd_user") ) -// GetUserBySubIss ... -func GetUserBySubIss(sub, issuer string) (*models.User, error) { - var oidcUsers []models.OIDCUser - n, err := GetOrmer().Raw(`select * from oidc_user where subiss = ? `, sub+issuer).QueryRows(&oidcUsers) - if err != nil { - return nil, err - } - if n == 0 { - return nil, nil - } - - user, err := GetUser(models.User{ - UserID: oidcUsers[0].UserID, - }) - if err != nil { - return nil, err - } - if user == nil { - return nil, fmt.Errorf("can not get user %d", oidcUsers[0].UserID) - } - - return user, nil -} - -// GetOIDCUserByUserID ... -func GetOIDCUserByUserID(userID int) (*models.OIDCUser, error) { - var oidcUsers []models.OIDCUser - n, err := GetOrmer().Raw(`select * from oidc_user where user_id = ? `, userID).QueryRows(&oidcUsers) - if err != nil { - return nil, err - } - if n == 0 { - return nil, nil - } - - return &oidcUsers[0], nil -} - // UpdateOIDCUser updates the OIDCUser based on the input parm, only the column "secret" and "token" can be updated func UpdateOIDCUser(oidcUser *models.OIDCUser) error { cols := []string{"secret", "token"} diff --git a/src/common/dao/oidc_user_test.go b/src/common/dao/oidc_user_test.go index ca1ef6e3a..48643d571 100644 --- a/src/common/dao/oidc_user_test.go +++ b/src/common/dao/oidc_user_test.go @@ -60,11 +60,6 @@ func TestOIDCUserMetaDaoMethods(t *testing.T) { require.NotNil(t, err) assert.Equal(t, "unable to onboard as empty oidc user", err.Error()) - // test get by sub and iss - userGetBySubIss, err := GetUserBySubIss("QWE123", "123RT1") - require.Nil(t, err) - assert.Equal(t, "user111@email.com", userGetBySubIss.Email) - // test update meta3 := &models.OIDCUser{ ID: ou111.ID, diff --git a/src/common/dao/user.go b/src/common/dao/user.go index cdd09b019..87f1800b1 100644 --- a/src/common/dao/user.go +++ b/src/common/dao/user.go @@ -15,13 +15,9 @@ package dao import ( - "errors" "fmt" - "time" "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/common/utils" - "github.com/goharbor/harbor/src/lib/log" ) // GetUser ... @@ -71,88 +67,6 @@ func GetUser(query models.User) (*models.User, error) { return &u[0], nil } -// LoginByDb is used for user to login with database auth mode. -func LoginByDb(auth models.AuthModel) (*models.User, error) { - var users []models.User - o := GetOrmer() - - n, err := o.Raw(`select * from harbor_user where (username = ? or email = ?) and deleted = false`, - auth.Principal, auth.Principal).QueryRows(&users) - if err != nil { - return nil, err - } - if n == 0 { - return nil, nil - } - - user := users[0] - - if !matchPassword(&user, auth.Password) { - return nil, nil - } - user.Password = "" // do not return the password - return &user, nil -} - -// ChangeUserPassword ... -func ChangeUserPassword(u models.User) error { - u.UpdateTime = time.Now() - u.Salt = utils.GenerateRandomString() - u.Password = utils.Encrypt(u.Password, u.Salt, utils.SHA256) - var err error - if u.PasswordVersion == utils.SHA1 { - u.PasswordVersion = utils.SHA256 - _, err = GetOrmer().Update(&u, "Password", "PasswordVersion", "Salt", "UpdateTime") - } else { - _, err = GetOrmer().Update(&u, "Password", "Salt", "UpdateTime") - } - return err -} - -// ResetUserPassword ... -func ResetUserPassword(u models.User, rawPassword string) error { - var rowsAffected int64 - var err error - u.UpdateTime = time.Now() - u.Password = utils.Encrypt(rawPassword, u.Salt, utils.SHA256) - u.ResetUUID = "" - if u.PasswordVersion == utils.SHA1 { - u.PasswordVersion = utils.SHA256 - rowsAffected, err = GetOrmer().Update(&u, "Password", "PasswordVersion", "ResetUUID", "UpdateTime") - } else { - rowsAffected, err = GetOrmer().Update(&u, "Password", "ResetUUID", "UpdateTime") - } - if err != nil { - return err - } - if rowsAffected == 0 { - return errors.New("no record be changed, reset password failed") - } - return nil -} - -// UpdateUserResetUUID ... -func UpdateUserResetUUID(u models.User) error { - o := GetOrmer() - _, err := o.Raw(`update harbor_user set reset_uuid=? where email=?`, u.ResetUUID, u.Email).Exec() - return err -} - -// ChangeUserProfile - Update user in local db, -// cols to specify the columns need to update, -// Email, and RealName, Comment are updated by default. -func ChangeUserProfile(user models.User, cols ...string) error { - o := GetOrmer() - if len(cols) == 0 { - cols = []string{"Email", "Realname", "Comment"} - } - if _, err := o.Update(&user, cols...); err != nil { - log.Errorf("update user failed, error: %v", err) - return err - } - return nil -} - // OnBoardUser will check if a user exists in user table, if not insert the user and // put the id in the pointer of user model, if it does exist, return the user's profile. // This is used for ldap and uaa authentication, such the user can have an ID in Harbor. @@ -185,26 +99,8 @@ func OnBoardUser(u *models.User) error { return nil } -// IsSuperUser checks if the user is super user(conventionally id == 1) of Harbor -func IsSuperUser(username string) bool { - u, err := GetUser(models.User{ - Username: username, - }) - log.Debugf("Check if user %s is super user", username) - if err != nil { - log.Errorf("Failed to get user from DB, username: %s, error: %v", username, err) - return false - } - return u != nil && u.UserID == 1 -} - // CleanUser - Clean this user information from DB func CleanUser(id int64) error { _, err := GetOrmer().QueryTable(&models.User{}).Filter("UserID", id).Delete() return err } - -// MatchPassword returns true is password matched -func matchPassword(u *models.User, password string) bool { - return utils.Encrypt(password, u.Salt, u.PasswordVersion) == u.Password -} diff --git a/src/common/models/user.go b/src/common/models/user.go index 9d9bf2e75..65c5ae896 100644 --- a/src/common/models/user.go +++ b/src/common/models/user.go @@ -15,7 +15,10 @@ package models import ( + "context" "time" + + "github.com/astaxie/beego/orm" ) // UserTable is the name of table in DB that holds the user object @@ -50,3 +53,20 @@ type User struct { func (u *User) TableName() string { return UserTable } + +// FilterByUsernameOrEmail generates the query setter to match username or email column to the same value +func (u *User) FilterByUsernameOrEmail(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter { + usernameOrEmail, ok := value.(string) + if !ok { + return qs + } + subCond := orm.NewCondition() + subCond = subCond.Or("Username", usernameOrEmail).Or("Email", usernameOrEmail) + + conds := qs.GetCond() + if conds == nil { + conds = orm.NewCondition() + } + qs = qs.SetCond(conds.AndCond(subCond)) + return qs +} diff --git a/src/common/utils/test/database.go b/src/common/utils/test/database.go index 5dd0a5421..fb46dc8eb 100644 --- a/src/common/utils/test/database.go +++ b/src/common/utils/test/database.go @@ -21,8 +21,9 @@ import ( "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/lib/orm" + pkguser "github.com/goharbor/harbor/src/pkg/user" ) // InitDatabaseFromEnv is used to initialize database for testing @@ -86,9 +87,7 @@ func updateUserInitialPassword(userID int, password string) error { return fmt.Errorf("user id: %d does not exist", userID) } if user.Salt == "" { - user.Salt = utils.GenerateRandomString() - user.Password = password - err = dao.ChangeUserPassword(*user) + err = pkguser.Mgr.UpdatePassword(orm.Context(), userID, password) if err != nil { return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err) } diff --git a/src/controller/user/controller.go b/src/controller/user/controller.go index 6a6b9524f..d646a61ea 100644 --- a/src/controller/user/controller.go +++ b/src/controller/user/controller.go @@ -37,7 +37,7 @@ type Controller interface { // SetSysAdmin ... SetSysAdmin(ctx context.Context, id int, adminFlag bool) error // VerifyPassword ... - VerifyPassword(ctx context.Context, username string, password string) (bool, error) + VerifyPassword(ctx context.Context, usernameOrEmail string, password string) (bool, error) // UpdatePassword ... UpdatePassword(ctx context.Context, id int, password string) error // List ... @@ -50,11 +50,13 @@ type Controller interface { Get(ctx context.Context, id int, opt *Option) (*models.User, error) // GetByName gets the user model by username, it only supports getting the basic and does not support opt GetByName(ctx context.Context, username string) (*models.User, error) + // GetBySubIss gets the user model by subject and issuer, the result will contain the basic user model and does not support opt + GetBySubIss(ctx context.Context, sub, iss string) (*models.User, error) // Delete ... Delete(ctx context.Context, id int) error // UpdateProfile update the profile based on the ID and data in the model in parm, only a subset of attributes in the model // will be update, see the implementation of manager. - UpdateProfile(ctx context.Context, u *models.User) error + UpdateProfile(ctx context.Context, u *models.User, cols ...string) error // SetCliSecret sets the OIDC CLI secret for a user SetCliSecret(ctx context.Context, id int, secret string) error } @@ -77,6 +79,14 @@ type controller struct { oidcMetaMgr oidc.MetaManager } +func (c *controller) GetBySubIss(ctx context.Context, sub, iss string) (*models.User, error) { + oidcMeta, err := c.oidcMetaMgr.GetBySubIss(ctx, sub, iss) + if err != nil { + return nil, err + } + return c.Get(ctx, oidcMeta.UserID, nil) +} + func (c *controller) GetByName(ctx context.Context, username string) (*models.User, error) { return c.mgr.GetByName(ctx, username) } @@ -89,8 +99,8 @@ func (c *controller) Create(ctx context.Context, u *models.User) (int, error) { return c.mgr.Create(ctx, u) } -func (c *controller) UpdateProfile(ctx context.Context, u *models.User) error { - return c.mgr.UpdateProfile(ctx, u) +func (c *controller) UpdateProfile(ctx context.Context, u *models.User, cols ...string) error { + return c.mgr.UpdateProfile(ctx, u, cols...) } func (c *controller) Get(ctx context.Context, id int, opt *Option) (*models.User, error) { @@ -132,8 +142,12 @@ func (c *controller) UpdatePassword(ctx context.Context, id int, password string return c.mgr.UpdatePassword(ctx, id, password) } -func (c *controller) VerifyPassword(ctx context.Context, username, password string) (bool, error) { - return c.mgr.VerifyLocalPassword(ctx, username, password) +func (c *controller) VerifyPassword(ctx context.Context, usernameOrEmail, password string) (bool, error) { + rec, err := c.mgr.MatchLocalPassword(ctx, usernameOrEmail, password) + if err != nil { + return false, err + } + return rec != nil, nil } func (c *controller) SetSysAdmin(ctx context.Context, id int, adminFlag bool) error { diff --git a/src/core/api/internal.go b/src/core/api/internal.go index 07e328994..6d6cd6518 100644 --- a/src/core/api/internal.go +++ b/src/core/api/internal.go @@ -21,9 +21,10 @@ import ( o "github.com/astaxie/beego/orm" "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/controller/quota" + "github.com/goharbor/harbor/src/controller/user" + "github.com/goharbor/harbor/src/core/auth" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" @@ -49,13 +50,14 @@ func (ia *InternalAPI) Prepare() { // RenameAdmin we don't provide flexibility in this API, as this is a workaround. func (ia *InternalAPI) RenameAdmin() { - if !dao.IsSuperUser(ia.SecurityCtx.GetUsername()) { + ctx := ia.Ctx.Request.Context() + if !auth.IsSuperUser(ctx, ia.SecurityCtx.GetUsername()) { log.Errorf("User %s is not super user, not allow to rename admin.", ia.SecurityCtx.GetUsername()) ia.SendForbiddenError(errors.New(ia.SecurityCtx.GetUsername())) return } newName := common.NewHarborAdminName - if err := dao.ChangeUserProfile(models.User{ + if err := user.Ctl.UpdateProfile(ctx, &models.User{ UserID: 1, Username: newName, }, "username"); err != nil { diff --git a/src/core/auth/authenticator.go b/src/core/auth/authenticator.go index b57a7d35a..a422205f5 100644 --- a/src/core/auth/authenticator.go +++ b/src/core/auth/authenticator.go @@ -15,19 +15,19 @@ package auth import ( + "context" "errors" "fmt" "time" + "github.com/goharbor/harbor/src/common" + "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/config" libErrors "github.com/goharbor/harbor/src/lib/errors" - "github.com/goharbor/harbor/src/lib/orm" - "github.com/goharbor/harbor/src/pkg/usergroup/model" - - "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/common/dao" - "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/goharbor/harbor/src/pkg/user" + "github.com/goharbor/harbor/src/pkg/usergroup/model" ) // 1.5 seconds @@ -137,12 +137,12 @@ func Register(name string, h AuthenticateHelper) { // Login authenticates user credentials based on setting. func Login(m models.AuthModel) (*models.User, error) { - - authMode, err := config.AuthMode(orm.Context()) + ctx := orm.Context() + authMode, err := config.AuthMode(ctx) if err != nil { return nil, err } - if authMode == "" || dao.IsSuperUser(m.Principal) { + if authMode == "" || IsSuperUser(ctx, m.Principal) { authMode = common.DBAuth } log.Debug("Current AUTH_MODE is ", authMode) @@ -257,3 +257,13 @@ func PostAuthenticate(u *models.User) error { } return helper.PostAuthenticate(u) } + +// IsSuperUser checks if the user is super user(conventionally id == 1) of Harbor +func IsSuperUser(ctx context.Context, username string) bool { + u, err := user.Mgr.GetByName(ctx, username) + if err != nil { + log.Errorf("Failed to get user from DB, username: %s, error: %v", username, err) + return false + } + return u.UserID == 1 +} diff --git a/src/core/auth/db/db.go b/src/core/auth/db/db.go index 405bf0d8a..2d0128b72 100644 --- a/src/core/auth/db/db.go +++ b/src/core/auth/db/db.go @@ -19,6 +19,8 @@ import ( "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/core/auth" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/goharbor/harbor/src/pkg/user" ) // Auth implements Authenticator interface to authenticate user against DB. @@ -28,7 +30,7 @@ type Auth struct { // Authenticate calls dao to authenticate user. func (d *Auth) Authenticate(m models.AuthModel) (*models.User, error) { - u, err := dao.LoginByDb(m) + u, err := user.Mgr.MatchLocalPassword(orm.Context(), m.Principal, m.Password) if err != nil { return nil, err } diff --git a/src/core/auth/ldap/ldap.go b/src/core/auth/ldap/ldap.go index add56ed51..3dc9366fd 100644 --- a/src/core/auth/ldap/ldap.go +++ b/src/core/auth/ldap/ldap.go @@ -283,7 +283,7 @@ func (l *Auth) PostAuthenticate(u *models.User) error { if !Re.MatchString(u.Email) { log.Debugf("Not a valid email address: %v, skip to sync", u.Email) } else { - if err = dao.ChangeUserProfile(*u, "Email"); err != nil { + if err = user.Mgr.UpdateProfile(ctx, u, "Email"); err != nil { u.Email = dbUser.Email log.Errorf("failed to sync user email: %v", err) } diff --git a/src/core/auth/uaa/uaa.go b/src/core/auth/uaa/uaa.go index 29106eddd..fae29160a 100644 --- a/src/core/auth/uaa/uaa.go +++ b/src/core/auth/uaa/uaa.go @@ -16,8 +16,6 @@ package uaa import ( "fmt" - "github.com/goharbor/harbor/src/lib/config" - "github.com/goharbor/harbor/src/lib/orm" "os" "strings" "sync" @@ -27,7 +25,10 @@ import ( "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/utils/uaa" "github.com/goharbor/harbor/src/core/auth" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/lib/orm" + userpkg "github.com/goharbor/harbor/src/pkg/user" ) // Auth is the implementation of AuthenticateHelper to access uaa for authentication. @@ -95,10 +96,9 @@ func (u *Auth) PostAuthenticate(user *models.User) error { user.UserID = dbUser.UserID user.SysAdminFlag = dbUser.SysAdminFlag fillEmailRealName(user) - if err2 := dao.ChangeUserProfile(*user, "Email", "Realname"); err2 != nil { + if err2 := userpkg.Mgr.UpdateProfile(orm.Context(), user, "Email", "Realname"); err2 != nil { log.Warningf("Failed to update user profile, user: %s, error: %v", user.Username, err2) } - return nil } diff --git a/src/core/controllers/base.go b/src/core/controllers/base.go index ba11c045f..ecec6d7f2 100644 --- a/src/core/controllers/base.go +++ b/src/core/controllers/base.go @@ -15,25 +15,18 @@ package controllers import ( - "bytes" "context" - "html/template" - "net" "net/http" "os" - "regexp" - "strconv" "strings" "github.com/astaxie/beego" o "github.com/astaxie/beego/orm" "github.com/beego/i18n" "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/security" - "github.com/goharbor/harbor/src/common/utils" - email_util "github.com/goharbor/harbor/src/common/utils/email" + "github.com/goharbor/harbor/src/controller/user" "github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/core/auth" "github.com/goharbor/harbor/src/lib" @@ -41,7 +34,6 @@ import ( "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/lib/q" - "github.com/goharbor/harbor/src/pkg/user" ) // CommonController handles request from UI that doesn't expect a page, such as /SwitchLanguage /logout ... @@ -68,18 +60,18 @@ func redirectForOIDC(ctx context.Context, username string) bool { if lib.GetAuthMode(ctx) != common.OIDCAuth { return false } - u, err := dao.GetUser(models.User{Username: username}) + u, err := user.Ctl.GetByName(ctx, 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) + us, err := user.Ctl.Get(ctx, u.UserID, &user.Option{WithOIDCInfo: true}) if err != nil { log.Warningf("Failed to get OIDC user info for user, id: %d, error: %v", u.UserID, err) } - if ou != nil { + if us != nil && us.OIDCUserMeta != nil { return true } return false @@ -149,7 +141,7 @@ func (cc *CommonController) UserExists() { query = q.New(q.KeyWords{"Email": value}) } - n, err := user.Mgr.Count(ctx, query) + n, err := user.Ctl.Count(ctx, query) if err != nil { log.Errorf("Error occurred in UserExists: %v", err) cc.CustomAbort(http.StatusInternalServerError, "Internal error.") @@ -158,145 +150,6 @@ func (cc *CommonController) UserExists() { cc.ServeJSON() } -// SendResetEmail verifies the Email address and contact SMTP server to send reset password Email. -func (cc *CommonController) SendResetEmail() { - - email := cc.GetString("email") - - valid, err := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email) - if err != nil { - log.Errorf("failed to match regexp: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "Internal error.") - } - - if !valid { - cc.CustomAbort(http.StatusBadRequest, "invalid email") - } - - queryUser := models.User{Email: email} - u, err := dao.GetUser(queryUser) - if err != nil { - log.Errorf("Error occurred in GetUser: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "Internal error.") - } - if u == nil { - log.Debugf("email %s not found", email) - cc.CustomAbort(http.StatusNotFound, "email_does_not_exist") - } - - if !isUserResetable(u) { - log.Errorf("Resetting password for user with ID: %d is not allowed", u.UserID) - cc.CustomAbort(http.StatusForbidden, http.StatusText(http.StatusForbidden)) - } - - uuid := utils.GenerateRandomString() - user := models.User{ResetUUID: uuid, Email: email} - if err = dao.UpdateUserResetUUID(user); err != nil { - log.Errorf("failed to update user reset UUID: %v", err) - cc.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - - messageTemplate, err := template.ParseFiles("views/reset-password-mail.tpl") - if err != nil { - log.Errorf("Parse email template file failed: %v", err) - cc.CustomAbort(http.StatusInternalServerError, err.Error()) - } - - message := new(bytes.Buffer) - - harborURL, err := config.ExtEndpoint() - if err != nil { - log.Errorf("failed to get domain name: %v", err) - cc.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - - err = messageTemplate.Execute(message, messageDetail{ - Hint: cc.Tr("reset_email_hint"), - URL: harborURL, - UUID: uuid, - }) - - if err != nil { - log.Errorf("Message template error: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "internal_error") - } - - settings, err := config.Email(orm.Context()) - if err != nil { - log.Errorf("failed to get email configurations: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "internal_error") - } - - addr := net.JoinHostPort(settings.Host, strconv.Itoa(settings.Port)) - err = email_util.Send(addr, - settings.Identity, - settings.Username, - settings.Password, - 60, settings.SSL, - settings.Insecure, - settings.From, - []string{u.Email}, - "Reset Harbor user password", - message.String()) - if err != nil { - log.Errorf("Send email failed: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "send_email_failed") - } -} - -// ResetPassword handles request from the reset page and reset password -func (cc *CommonController) ResetPassword() { - - resetUUID := cc.GetString("reset_uuid") - if resetUUID == "" { - cc.CustomAbort(http.StatusBadRequest, "Reset uuid is blank.") - } - - queryUser := models.User{ResetUUID: resetUUID} - user, err := dao.GetUser(queryUser) - - if err != nil { - log.Errorf("Error occurred in GetUser: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "Internal error.") - } - if user == nil { - log.Error("User does not exist") - cc.CustomAbort(http.StatusBadRequest, "User does not exist") - } - - if !isUserResetable(user) { - log.Errorf("Resetting password for user with ID: %d is not allowed", user.UserID) - cc.CustomAbort(http.StatusForbidden, http.StatusText(http.StatusForbidden)) - } - - rawPassword := cc.GetString("password") - - if rawPassword != "" { - err = dao.ResetUserPassword(*user, rawPassword) - if err != nil { - log.Errorf("Error occurred in ResetUserPassword: %v", err) - cc.CustomAbort(http.StatusInternalServerError, "Internal error.") - } - } else { - cc.CustomAbort(http.StatusBadRequest, "password_is_required") - } -} - -func isUserResetable(u *models.User) bool { - if u == nil { - return false - } - mode, err := config.AuthMode(orm.Context()) - if err != nil { - log.Errorf("Failed to get the auth mode, error: %v", err) - return false - } - if mode == common.DBAuth { - return true - } - return u.UserID == 1 -} - func init() { // conf/app.conf -> os.Getenv("config_path") configPath := os.Getenv("CONFIG_PATH") diff --git a/src/core/controllers/controllers_test.go b/src/core/controllers/controllers_test.go index 865db5ae0..258e2c3c6 100644 --- a/src/core/controllers/controllers_test.go +++ b/src/core/controllers/controllers_test.go @@ -15,10 +15,6 @@ package controllers import ( "fmt" - "github.com/goharbor/harbor/src/core/middlewares" - "github.com/goharbor/harbor/src/lib" - "github.com/goharbor/harbor/src/lib/config" - "github.com/goharbor/harbor/src/lib/orm" "net/http" "net/http/httptest" "os" @@ -27,9 +23,13 @@ import ( "strings" "testing" + "github.com/goharbor/harbor/src/core/middlewares" + "github.com/goharbor/harbor/src/lib" + "github.com/goharbor/harbor/src/lib/config" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/astaxie/beego" "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/common/models" utilstest "github.com/goharbor/harbor/src/common/utils/test" _ "github.com/goharbor/harbor/src/pkg/config/db" _ "github.com/goharbor/harbor/src/pkg/config/inmemory" @@ -58,35 +58,6 @@ func TestMain(m *testing.M) { } } -// TestUserResettable -func TestUserResettable(t *testing.T) { - assert := assert.New(t) - DBAuthConfig := map[string]interface{}{ - common.AUTHMode: common.DBAuth, - common.TokenExpiration: 30, - } - - LDAPAuthConfig := map[string]interface{}{ - common.AUTHMode: common.LDAPAuth, - common.TokenExpiration: 30, - } - config.InitWithSettings(LDAPAuthConfig) - u1 := &models.User{ - UserID: 3, - Username: "daniel", - Email: "daniel@test.com", - } - u2 := &models.User{ - UserID: 1, - Username: "jack", - Email: "jack@test.com", - } - assert.False(isUserResetable(u1)) - assert.True(isUserResetable(u2)) - config.InitWithSettings(DBAuthConfig) - assert.True(isUserResetable(u1)) -} - func TestRedirectForOIDC(t *testing.T) { ctx := lib.WithAuthMode(orm.Context(), common.DBAuth) assert.False(t, redirectForOIDC(ctx, "nonexist")) diff --git a/src/core/controllers/oidc.go b/src/core/controllers/oidc.go index 57ccc8a75..6e072900d 100644 --- a/src/core/controllers/oidc.go +++ b/src/core/controllers/oidc.go @@ -17,8 +17,6 @@ package controllers import ( "encoding/json" "fmt" - "github.com/goharbor/harbor/src/lib/config" - "github.com/goharbor/harbor/src/lib/orm" "net/http" "strings" @@ -26,9 +24,12 @@ import ( "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/utils" + "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/lib/orm" "github.com/goharbor/harbor/src/pkg/oidc" ) @@ -110,31 +111,23 @@ func (oc *OIDCController) Callback() { oc.SendInternalServerError(err) return } - u, err := dao.GetUserBySubIss(info.Subject, info.Issuer) - if err != nil { - oc.SendInternalServerError(err) - return - } tokenBytes, err := json.Marshal(token) if err != nil { oc.SendInternalServerError(err) return } oc.SetSession(tokenKey, tokenBytes) - - oidcSettings, err := config.OIDCSetting(ctx) - if err != nil { - oc.SendInternalServerError(err) - return - } - - if u == nil { + u, err := user.Ctl.GetBySubIss(ctx, info.Subject, info.Issuer) + if errors.IsNotFoundErr(err) { // User is not onboarded, kickoff the onboard flow // Recover the username from d.Username by default username := info.Username - // Fix blanks in username username = strings.Replace(username, " ", "_", -1) - + oidcSettings, err := config.OIDCSetting(ctx) + if err != nil { + oc.SendInternalServerError(err) + return + } // If automatic onboard is enabled, skip the onboard page if oidcSettings.AutoOnboard { log.Debug("Doing automatic onboarding\n") @@ -143,27 +136,31 @@ func (oc *OIDCController) Callback() { oidcSettings.UserClaim)) return } - user, onboarded := userOnboard(oc, info, username, tokenBytes) + userRec, onboarded := userOnboard(oc, info, username, tokenBytes) if onboarded == false { log.Error("User not onboarded\n") return } log.Debug("User automatically onboarded\n") - u = user + u = userRec } else { oc.SetSession(userInfoKey, string(ouDataStr)) oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", username), http.StatusFound) // Once redirected, no further actions are done return } + } else if err != nil { + oc.SendInternalServerError(err) + return } oidc.InjectGroupsToUser(info, u) - oidcUser, err := dao.GetOIDCUserByUserID(u.UserID) + um, err := user.Ctl.Get(ctx, u.UserID, &user.Option{WithOIDCInfo: true}) if err != nil { oc.SendInternalServerError(err) return } _, t, err := secretAndToken(tokenBytes) + oidcUser := um.OIDCUserMeta oidcUser.Token = t if err := dao.UpdateOIDCUser(oidcUser); err != nil { oc.SendInternalServerError(err) @@ -171,7 +168,6 @@ func (oc *OIDCController) Callback() { } oc.PopulateUserSession(*u) oc.Controller.Redirect("/", http.StatusFound) - } func userOnboard(oc *OIDCController, info *oidc.UserInfo, username string, tokenBytes []byte) (*models.User, bool) { diff --git a/src/core/main.go b/src/core/main.go index 44f4d9c7b..a4bbdcf6d 100755 --- a/src/core/main.go +++ b/src/core/main.go @@ -32,10 +32,10 @@ import ( "github.com/goharbor/harbor/src/common/dao" common_http "github.com/goharbor/harbor/src/common/http" "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/common/utils" _ "github.com/goharbor/harbor/src/controller/event/handler" "github.com/goharbor/harbor/src/controller/health" "github.com/goharbor/harbor/src/controller/registry" + ctluser "github.com/goharbor/harbor/src/controller/user" "github.com/goharbor/harbor/src/core/api" _ "github.com/goharbor/harbor/src/core/auth/authproxy" _ "github.com/goharbor/harbor/src/core/auth/db" @@ -64,7 +64,7 @@ const ( adminUserID = 1 ) -func updateInitPassword(userID int, password string) error { +func updateInitPassword(ctx context.Context, userID int, password string) error { queryUser := models.User{UserID: userID} user, err := dao.GetUser(queryUser) if err != nil { @@ -74,11 +74,7 @@ func updateInitPassword(userID int, password string) error { return fmt.Errorf("user id: %d does not exist", userID) } if user.Salt == "" { - salt := utils.GenerateRandomString() - - user.Salt = salt - user.Password = password - err = dao.ChangeUserPassword(*user) + err = ctluser.Ctl.UpdatePassword(ctx, userID, password) if err != nil { return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err) } @@ -190,7 +186,8 @@ func main() { if err = migration.Migrate(database); err != nil { log.Fatalf("failed to migrate: %v", err) } - if err := config.Load(orm.Context()); err != nil { + ctx := orm.Context() + if err := config.Load(ctx); err != nil { log.Fatalf("failed to load config: %v", err) } @@ -198,7 +195,7 @@ func main() { if err != nil { log.Fatalf("failed to get admin's initial password: %v", err) } - if err := updateInitPassword(adminUserID, password); err != nil { + if err := updateInitPassword(ctx, adminUserID, password); err != nil { log.Error(err) } diff --git a/src/pkg/oidc/metamanager.go b/src/pkg/oidc/metamanager.go index 04c899155..d6925a1cd 100644 --- a/src/pkg/oidc/metamanager.go +++ b/src/pkg/oidc/metamanager.go @@ -31,6 +31,8 @@ type MetaManager interface { Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error) // GetByUserID gets the oidc meta record by user's ID GetByUserID(ctx context.Context, uid int) (*models.OIDCUser, error) + // GetBySubIss gets the oidc meta record by the subject and issuer + GetBySubIss(ctx context.Context, sub, iss string) (*models.OIDCUser, error) // SetCliSecretByUserID updates the cli secret of a user based on the user ID SetCliSecretByUserID(ctx context.Context, uid int, secret string) error } @@ -39,6 +41,21 @@ type metaManager struct { dao dao.MetaDAO } +func (m *metaManager) GetBySubIss(ctx context.Context, sub, iss string) (*models.OIDCUser, error) { + logger := log.GetLogger(ctx) + l, err := m.dao.List(ctx, q.New(q.KeyWords{"subiss": sub + iss})) + if err != nil { + return nil, err + } + if len(l) == 0 { + return nil, errors.NotFoundError(nil).WithMessage("oidc info for user with issuer %s, subject %s not found", iss, sub) + } + if len(l) > 1 { + logger.Warningf("Multiple oidc info records found for issuer %s, subject %s", iss, sub) + } + return l[0], nil +} + func (m *metaManager) Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error) { return m.dao.Create(ctx, oidcUser) } diff --git a/src/pkg/user/dao/dao_test.go b/src/pkg/user/dao/dao_test.go index b38977dec..884be515f 100644 --- a/src/pkg/user/dao/dao_test.go +++ b/src/pkg/user/dao/dao_test.go @@ -74,17 +74,43 @@ func (suite *DaoTestSuite) TestCount() { } func (suite *DaoTestSuite) TestList() { + ctx := orm.Context() { - users, err := suite.dao.List(orm.Context(), q.New(q.KeyWords{"user_id": 1})) + users, err := suite.dao.List(ctx, q.New(q.KeyWords{"user_id": 1})) suite.Nil(err) suite.Len(users, 1) } { - users, err := suite.dao.List(orm.Context(), q.New(q.KeyWords{"username": "admin"})) + users, err := suite.dao.List(ctx, q.New(q.KeyWords{"username": "admin"})) suite.Nil(err) suite.Len(users, 1) } + id, err := suite.dao.Create(ctx, &models.User{ + Username: "list_test", + Realname: "list test", + Email: "list_test@test.com", + Password: "somepassword", + PasswordVersion: "sha256", + }) + suite.appendClearSQL(id) + suite.Nil(err) + { + users, err := suite.dao.List(ctx, q.New(q.KeyWords{"username_or_email": "list_test"})) + suite.Nil(err) + suite.Len(users, 1) + } + { + users, err := suite.dao.List(ctx, q.New(q.KeyWords{"username_or_email": "list_test@test.com"})) + suite.Nil(err) + suite.Len(users, 1) + } + { + users, err := suite.dao.List(ctx, q.New(q.KeyWords{"username_or_email": "noremail_norusername"})) + suite.Nil(err) + suite.Len(users, 0) + } + } func (suite *DaoTestSuite) TestCreate() { diff --git a/src/pkg/user/manager.go b/src/pkg/user/manager.go index 87687f3c7..57465a48a 100644 --- a/src/pkg/user/manager.go +++ b/src/pkg/user/manager.go @@ -35,7 +35,7 @@ var ( type Manager interface { // Get get user by user id Get(ctx context.Context, id int) (*models.User, error) - // GetByName get user by username + // GetByName get user by username, it will return an error if the user does not exist GetByName(ctx context.Context, username string) (*models.User, error) // List users according to the query List(ctx context.Context, query *q.Query) (models.Users, error) @@ -48,11 +48,12 @@ type Manager interface { // SetSysAdminFlag sets the system admin flag of the user in local DB SetSysAdminFlag(ctx context.Context, id int, admin bool) error // UpdateProfile updates the user's profile - UpdateProfile(ctx context.Context, user *models.User) error + UpdateProfile(ctx context.Context, user *models.User, col ...string) error // UpdatePassword updates user's password UpdatePassword(ctx context.Context, id int, newPassword string) error - // VerifyLocalPassword verifies the password against the record in DB based on the input - VerifyLocalPassword(ctx context.Context, username, password string) (bool, error) + // MatchLocalPassword tries to match the record in DB based on the input, the first return value is + // the user model corresponding to the entry in DB + MatchLocalPassword(ctx context.Context, username, password string) (*models.User, error) } // New returns a default implementation of Manager @@ -75,20 +76,29 @@ func (m *manager) Delete(ctx context.Context, id int) error { return m.dao.Update(ctx, u, "username", "email", "deleted") } -func (m *manager) VerifyLocalPassword(ctx context.Context, username, password string) (bool, error) { - u, err := m.GetByName(ctx, username) +func (m *manager) MatchLocalPassword(ctx context.Context, usernameOrEmail, password string) (*models.User, error) { + l, err := m.dao.List(ctx, q.New(q.KeyWords{"username_or_email": usernameOrEmail})) if err != nil { - return false, err + return nil, err } - return utils.Encrypt(password, u.Salt, u.PasswordVersion) == u.Password, nil + for _, entry := range l { + if utils.Encrypt(password, entry.Salt, entry.PasswordVersion) == entry.Password { + entry.Password = "" + return entry, nil + } + } + return nil, nil } func (m *manager) Count(ctx context.Context, query *q.Query) (int64, error) { return m.dao.Count(ctx, query) } -func (m *manager) UpdateProfile(ctx context.Context, user *models.User) error { - return m.dao.Update(ctx, user, "email", "realname", "comment") +func (m *manager) UpdateProfile(ctx context.Context, user *models.User, cols ...string) error { + if cols == nil || len(cols) == 0 { + cols = []string{"Email", "Realname", "Comment"} + } + return m.dao.Update(ctx, user, cols...) } func (m *manager) UpdatePassword(ctx context.Context, id int, newPassword string) error { @@ -126,7 +136,7 @@ func (m *manager) Get(ctx context.Context, id int) (*models.User, error) { return users[0], nil } -// Get get user by username +// GetByName get user by username func (m *manager) GetByName(ctx context.Context, username string) (*models.User, error) { users, err := m.dao.List(ctx, q.New(q.KeyWords{"username": username})) if err != nil { diff --git a/src/server/middleware/security/idtoken.go b/src/server/middleware/security/idtoken.go index 9444852bb..73a84024f 100644 --- a/src/server/middleware/security/idtoken.go +++ b/src/server/middleware/security/idtoken.go @@ -15,15 +15,15 @@ package security import ( - "github.com/goharbor/harbor/src/lib/config" "net/http" "strings" "github.com/goharbor/harbor/src/common" - "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/security" "github.com/goharbor/harbor/src/common/security/local" + "github.com/goharbor/harbor/src/controller/user" "github.com/goharbor/harbor/src/lib" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/pkg/oidc" ) @@ -48,15 +48,11 @@ func (i *idToken) Generate(req *http.Request) security.Context { log.Warningf("failed to verify token: %v", err) return nil } - u, err := dao.GetUserBySubIss(claims.Subject, claims.Issuer) + u, err := user.Ctl.GetBySubIss(ctx, claims.Subject, claims.Issuer) if err != nil { log.Warningf("failed to get user based on token claims: %v", err) return nil } - if u == nil { - log.Warning("user matches token's claims is not onboarded.") - return nil - } setting, err := config.OIDCSetting(ctx) if err != nil { log.Errorf("failed to get OIDC settings: %v", err) diff --git a/src/testing/controller/user/controller.go b/src/testing/controller/user/controller.go index ed74255bb..510dd678d 100644 --- a/src/testing/controller/user/controller.go +++ b/src/testing/controller/user/controller.go @@ -120,6 +120,29 @@ func (_m *Controller) GetByName(ctx context.Context, username string) (*models.U return r0, r1 } +// GetBySubIss provides a mock function with given fields: ctx, sub, iss +func (_m *Controller) GetBySubIss(ctx context.Context, sub string, iss string) (*models.User, error) { + ret := _m.Called(ctx, sub, iss) + + var r0 *models.User + if rf, ok := ret.Get(0).(func(context.Context, string, string) *models.User); ok { + r0 = rf(ctx, sub, iss) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, sub, iss) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // List provides a mock function with given fields: ctx, query func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*models.User, error) { ret := _m.Called(ctx, query) @@ -185,13 +208,20 @@ func (_m *Controller) UpdatePassword(ctx context.Context, id int, password strin return r0 } -// UpdateProfile provides a mock function with given fields: ctx, u -func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User) error { - ret := _m.Called(ctx, u) +// UpdateProfile provides a mock function with given fields: ctx, u, cols +func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User, cols ...string) error { + _va := make([]interface{}, len(cols)) + for _i := range cols { + _va[_i] = cols[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, u) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *models.User) error); ok { - r0 = rf(ctx, u) + if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok { + r0 = rf(ctx, u, cols...) } else { r0 = ret.Error(0) } @@ -199,20 +229,20 @@ func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User) error { return r0 } -// VerifyPassword provides a mock function with given fields: ctx, username, password -func (_m *Controller) VerifyPassword(ctx context.Context, username string, password string) (bool, error) { - ret := _m.Called(ctx, username, password) +// VerifyPassword provides a mock function with given fields: ctx, usernameOrEmail, password +func (_m *Controller) VerifyPassword(ctx context.Context, usernameOrEmail string, password string) (bool, error) { + ret := _m.Called(ctx, usernameOrEmail, password) var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { - r0 = rf(ctx, username, password) + r0 = rf(ctx, usernameOrEmail, password) } else { r0 = ret.Get(0).(bool) } var r1 error if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, username, password) + r1 = rf(ctx, usernameOrEmail, password) } else { r1 = ret.Error(1) } diff --git a/src/testing/pkg/user/manager.go b/src/testing/pkg/user/manager.go index fa1a04006..ae19ec1fa 100644 --- a/src/testing/pkg/user/manager.go +++ b/src/testing/pkg/user/manager.go @@ -143,6 +143,29 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) (usermodels.Users, return r0, r1 } +// MatchLocalPassword provides a mock function with given fields: ctx, username, password +func (_m *Manager) MatchLocalPassword(ctx context.Context, username string, password string) (*models.User, error) { + ret := _m.Called(ctx, username, password) + + var r0 *models.User + if rf, ok := ret.Get(0).(func(context.Context, string, string) *models.User); ok { + r0 = rf(ctx, username, password) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, username, password) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SetSysAdminFlag provides a mock function with given fields: ctx, id, admin func (_m *Manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) error { ret := _m.Called(ctx, id, admin) @@ -171,37 +194,23 @@ func (_m *Manager) UpdatePassword(ctx context.Context, id int, newPassword strin return r0 } -// UpdateProfile provides a mock function with given fields: ctx, _a1 -func (_m *Manager) UpdateProfile(ctx context.Context, _a1 *models.User) error { - ret := _m.Called(ctx, _a1) +// UpdateProfile provides a mock function with given fields: ctx, _a1, col +func (_m *Manager) UpdateProfile(ctx context.Context, _a1 *models.User, col ...string) error { + _va := make([]interface{}, len(col)) + for _i := range col { + _va[_i] = col[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *models.User) error); ok { - r0 = rf(ctx, _a1) + if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok { + r0 = rf(ctx, _a1, col...) } else { r0 = ret.Error(0) } return r0 } - -// VerifyLocalPassword provides a mock function with given fields: ctx, username, password -func (_m *Manager) VerifyLocalPassword(ctx context.Context, username string, password string) (bool, error) { - ret := _m.Called(ctx, username, password) - - var r0 bool - if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { - r0 = rf(ctx, username, password) - } else { - r0 = ret.Get(0).(bool) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, username, password) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -}