mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-29 22:07:32 +02:00
Merge pull request #14829 from reasonerjt/user-common-dao-cleanup
Move user related funcs from common/dao
This commit is contained in:
commit
bd9a1c6722
@ -23,7 +23,6 @@ import (
|
|||||||
|
|
||||||
"github.com/astaxie/beego/orm"
|
"github.com/astaxie/beego/orm"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"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/log"
|
||||||
libOrm "github.com/goharbor/harbor/src/lib/orm"
|
libOrm "github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"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
|
var currentUser *models.User
|
||||||
|
|
||||||
func TestGetUser(t *testing.T) {
|
func TestGetUser(t *testing.T) {
|
||||||
@ -217,60 +177,6 @@ func TestGetUser(t *testing.T) {
|
|||||||
assert.NotNil(t, err)
|
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) {
|
func TestAddProject(t *testing.T) {
|
||||||
|
|
||||||
project := models.Project{
|
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
|
var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64
|
||||||
|
|
||||||
func TestGetOrmer(t *testing.T) {
|
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) {
|
func TestIsDupRecError(t *testing.T) {
|
||||||
assert.True(t, IsDupRecErr(fmt.Errorf("pq: duplicate key value violates unique constraint \"properties_k_key\"")))
|
assert.True(t, IsDupRecErr(fmt.Errorf("pq: duplicate key value violates unique constraint \"properties_k_key\"")))
|
||||||
assert.False(t, IsDupRecErr(fmt.Errorf("other error")))
|
assert.False(t, IsDupRecErr(fmt.Errorf("other error")))
|
||||||
|
@ -39,44 +39,6 @@ var (
|
|||||||
ErrRollBackOIDCUser = errors.New("sql: transaction roll back error in oicd_user")
|
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
|
// UpdateOIDCUser updates the OIDCUser based on the input parm, only the column "secret" and "token" can be updated
|
||||||
func UpdateOIDCUser(oidcUser *models.OIDCUser) error {
|
func UpdateOIDCUser(oidcUser *models.OIDCUser) error {
|
||||||
cols := []string{"secret", "token"}
|
cols := []string{"secret", "token"}
|
||||||
|
@ -60,11 +60,6 @@ func TestOIDCUserMetaDaoMethods(t *testing.T) {
|
|||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
assert.Equal(t, "unable to onboard as empty oidc user", err.Error())
|
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
|
// test update
|
||||||
meta3 := &models.OIDCUser{
|
meta3 := &models.OIDCUser{
|
||||||
ID: ou111.ID,
|
ID: ou111.ID,
|
||||||
|
@ -15,13 +15,9 @@
|
|||||||
package dao
|
package dao
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUser ...
|
// GetUser ...
|
||||||
@ -71,88 +67,6 @@ func GetUser(query models.User) (*models.User, error) {
|
|||||||
return &u[0], nil
|
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
|
// 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.
|
// 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.
|
// 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
|
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
|
// CleanUser - Clean this user information from DB
|
||||||
func CleanUser(id int64) error {
|
func CleanUser(id int64) error {
|
||||||
_, err := GetOrmer().QueryTable(&models.User{}).Filter("UserID", id).Delete()
|
_, err := GetOrmer().QueryTable(&models.User{}).Filter("UserID", id).Delete()
|
||||||
return err
|
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
|
|
||||||
}
|
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/orm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserTable is the name of table in DB that holds the user object
|
// 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 {
|
func (u *User) TableName() string {
|
||||||
return UserTable
|
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
|
||||||
|
}
|
||||||
|
@ -21,8 +21,9 @@ import (
|
|||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"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/log"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
|
pkguser "github.com/goharbor/harbor/src/pkg/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitDatabaseFromEnv is used to initialize database for testing
|
// 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)
|
return fmt.Errorf("user id: %d does not exist", userID)
|
||||||
}
|
}
|
||||||
if user.Salt == "" {
|
if user.Salt == "" {
|
||||||
user.Salt = utils.GenerateRandomString()
|
err = pkguser.Mgr.UpdatePassword(orm.Context(), userID, password)
|
||||||
user.Password = password
|
|
||||||
err = dao.ChangeUserPassword(*user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ type Controller interface {
|
|||||||
// SetSysAdmin ...
|
// SetSysAdmin ...
|
||||||
SetSysAdmin(ctx context.Context, id int, adminFlag bool) error
|
SetSysAdmin(ctx context.Context, id int, adminFlag bool) error
|
||||||
// VerifyPassword ...
|
// VerifyPassword ...
|
||||||
VerifyPassword(ctx context.Context, username string, password string) (bool, error)
|
VerifyPassword(ctx context.Context, usernameOrEmail string, password string) (bool, error)
|
||||||
// UpdatePassword ...
|
// UpdatePassword ...
|
||||||
UpdatePassword(ctx context.Context, id int, password string) error
|
UpdatePassword(ctx context.Context, id int, password string) error
|
||||||
// List ...
|
// List ...
|
||||||
@ -50,11 +50,13 @@ type Controller interface {
|
|||||||
Get(ctx context.Context, id int, opt *Option) (*models.User, error)
|
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 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)
|
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 ...
|
||||||
Delete(ctx context.Context, id int) error
|
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
|
// 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.
|
// 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 sets the OIDC CLI secret for a user
|
||||||
SetCliSecret(ctx context.Context, id int, secret string) error
|
SetCliSecret(ctx context.Context, id int, secret string) error
|
||||||
}
|
}
|
||||||
@ -77,6 +79,14 @@ type controller struct {
|
|||||||
oidcMetaMgr oidc.MetaManager
|
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) {
|
func (c *controller) GetByName(ctx context.Context, username string) (*models.User, error) {
|
||||||
return c.mgr.GetByName(ctx, username)
|
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)
|
return c.mgr.Create(ctx, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) UpdateProfile(ctx context.Context, u *models.User) error {
|
func (c *controller) UpdateProfile(ctx context.Context, u *models.User, cols ...string) error {
|
||||||
return c.mgr.UpdateProfile(ctx, u)
|
return c.mgr.UpdateProfile(ctx, u, cols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) Get(ctx context.Context, id int, opt *Option) (*models.User, error) {
|
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)
|
return c.mgr.UpdatePassword(ctx, id, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) VerifyPassword(ctx context.Context, username, password string) (bool, error) {
|
func (c *controller) VerifyPassword(ctx context.Context, usernameOrEmail, password string) (bool, error) {
|
||||||
return c.mgr.VerifyLocalPassword(ctx, username, password)
|
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 {
|
func (c *controller) SetSysAdmin(ctx context.Context, id int, adminFlag bool) error {
|
||||||
|
@ -21,9 +21,10 @@ import (
|
|||||||
|
|
||||||
o "github.com/astaxie/beego/orm"
|
o "github.com/astaxie/beego/orm"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"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/models"
|
||||||
"github.com/goharbor/harbor/src/controller/quota"
|
"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/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"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.
|
// RenameAdmin we don't provide flexibility in this API, as this is a workaround.
|
||||||
func (ia *InternalAPI) RenameAdmin() {
|
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())
|
log.Errorf("User %s is not super user, not allow to rename admin.", ia.SecurityCtx.GetUsername())
|
||||||
ia.SendForbiddenError(errors.New(ia.SecurityCtx.GetUsername()))
|
ia.SendForbiddenError(errors.New(ia.SecurityCtx.GetUsername()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newName := common.NewHarborAdminName
|
newName := common.NewHarborAdminName
|
||||||
if err := dao.ChangeUserProfile(models.User{
|
if err := user.Ctl.UpdateProfile(ctx, &models.User{
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
Username: newName,
|
Username: newName,
|
||||||
}, "username"); err != nil {
|
}, "username"); err != nil {
|
||||||
|
@ -15,19 +15,19 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
libErrors "github.com/goharbor/harbor/src/lib/errors"
|
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/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
|
// 1.5 seconds
|
||||||
@ -137,12 +137,12 @@ func Register(name string, h AuthenticateHelper) {
|
|||||||
|
|
||||||
// Login authenticates user credentials based on setting.
|
// Login authenticates user credentials based on setting.
|
||||||
func Login(m models.AuthModel) (*models.User, error) {
|
func Login(m models.AuthModel) (*models.User, error) {
|
||||||
|
ctx := orm.Context()
|
||||||
authMode, err := config.AuthMode(orm.Context())
|
authMode, err := config.AuthMode(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if authMode == "" || dao.IsSuperUser(m.Principal) {
|
if authMode == "" || IsSuperUser(ctx, m.Principal) {
|
||||||
authMode = common.DBAuth
|
authMode = common.DBAuth
|
||||||
}
|
}
|
||||||
log.Debug("Current AUTH_MODE is ", authMode)
|
log.Debug("Current AUTH_MODE is ", authMode)
|
||||||
@ -257,3 +257,13 @@ func PostAuthenticate(u *models.User) error {
|
|||||||
}
|
}
|
||||||
return helper.PostAuthenticate(u)
|
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
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
"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.
|
// Auth implements Authenticator interface to authenticate user against DB.
|
||||||
@ -28,7 +30,7 @@ type Auth struct {
|
|||||||
|
|
||||||
// Authenticate calls dao to authenticate user.
|
// Authenticate calls dao to authenticate user.
|
||||||
func (d *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ func (l *Auth) PostAuthenticate(u *models.User) error {
|
|||||||
if !Re.MatchString(u.Email) {
|
if !Re.MatchString(u.Email) {
|
||||||
log.Debugf("Not a valid email address: %v, skip to sync", u.Email)
|
log.Debugf("Not a valid email address: %v, skip to sync", u.Email)
|
||||||
} else {
|
} else {
|
||||||
if err = dao.ChangeUserProfile(*u, "Email"); err != nil {
|
if err = user.Mgr.UpdateProfile(ctx, u, "Email"); err != nil {
|
||||||
u.Email = dbUser.Email
|
u.Email = dbUser.Email
|
||||||
log.Errorf("failed to sync user email: %v", err)
|
log.Errorf("failed to sync user email: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@ package uaa
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -27,7 +25,10 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/uaa"
|
"github.com/goharbor/harbor/src/common/utils/uaa"
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
"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/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.
|
// 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.UserID = dbUser.UserID
|
||||||
user.SysAdminFlag = dbUser.SysAdminFlag
|
user.SysAdminFlag = dbUser.SysAdminFlag
|
||||||
fillEmailRealName(user)
|
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)
|
log.Warningf("Failed to update user profile, user: %s, error: %v", user.Username, err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,25 +15,18 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"html/template"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
o "github.com/astaxie/beego/orm"
|
o "github.com/astaxie/beego/orm"
|
||||||
"github.com/beego/i18n"
|
"github.com/beego/i18n"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"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/models"
|
||||||
"github.com/goharbor/harbor/src/common/security"
|
"github.com/goharbor/harbor/src/common/security"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"github.com/goharbor/harbor/src/controller/user"
|
||||||
email_util "github.com/goharbor/harbor/src/common/utils/email"
|
|
||||||
"github.com/goharbor/harbor/src/core/api"
|
"github.com/goharbor/harbor/src/core/api"
|
||||||
"github.com/goharbor/harbor/src/core/auth"
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
"github.com/goharbor/harbor/src/lib"
|
"github.com/goharbor/harbor/src/lib"
|
||||||
@ -41,7 +34,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"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 ...
|
// 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 {
|
if lib.GetAuthMode(ctx) != common.OIDCAuth {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
u, err := dao.GetUser(models.User{Username: username})
|
u, err := user.Ctl.GetByName(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to get user by name: %s, error: %v", username, err)
|
log.Warningf("Failed to get user by name: %s, error: %v", username, err)
|
||||||
}
|
}
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ou, err := dao.GetOIDCUserByUserID(u.UserID)
|
us, err := user.Ctl.Get(ctx, u.UserID, &user.Option{WithOIDCInfo: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to get OIDC user info for user, id: %d, error: %v", u.UserID, err)
|
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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -149,7 +141,7 @@ func (cc *CommonController) UserExists() {
|
|||||||
query = q.New(q.KeyWords{"Email": value})
|
query = q.New(q.KeyWords{"Email": value})
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := user.Mgr.Count(ctx, query)
|
n, err := user.Ctl.Count(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error occurred in UserExists: %v", err)
|
log.Errorf("Error occurred in UserExists: %v", err)
|
||||||
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
cc.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||||
@ -158,145 +150,6 @@ func (cc *CommonController) UserExists() {
|
|||||||
cc.ServeJSON()
|
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() {
|
func init() {
|
||||||
// conf/app.conf -> os.Getenv("config_path")
|
// conf/app.conf -> os.Getenv("config_path")
|
||||||
configPath := os.Getenv("CONFIG_PATH")
|
configPath := os.Getenv("CONFIG_PATH")
|
||||||
|
@ -15,10 +15,6 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@ -27,9 +23,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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/astaxie/beego"
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
|
||||||
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
utilstest "github.com/goharbor/harbor/src/common/utils/test"
|
||||||
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
||||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
_ "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) {
|
func TestRedirectForOIDC(t *testing.T) {
|
||||||
ctx := lib.WithAuthMode(orm.Context(), common.DBAuth)
|
ctx := lib.WithAuthMode(orm.Context(), common.DBAuth)
|
||||||
assert.False(t, redirectForOIDC(ctx, "nonexist"))
|
assert.False(t, redirectForOIDC(ctx, "nonexist"))
|
||||||
|
@ -17,8 +17,6 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -26,9 +24,12 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"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/core/api"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/pkg/oidc"
|
"github.com/goharbor/harbor/src/pkg/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -110,31 +111,23 @@ func (oc *OIDCController) Callback() {
|
|||||||
oc.SendInternalServerError(err)
|
oc.SendInternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u, err := dao.GetUserBySubIss(info.Subject, info.Issuer)
|
|
||||||
if err != nil {
|
|
||||||
oc.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tokenBytes, err := json.Marshal(token)
|
tokenBytes, err := json.Marshal(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oc.SendInternalServerError(err)
|
oc.SendInternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
oc.SetSession(tokenKey, tokenBytes)
|
oc.SetSession(tokenKey, tokenBytes)
|
||||||
|
u, err := user.Ctl.GetBySubIss(ctx, info.Subject, info.Issuer)
|
||||||
oidcSettings, err := config.OIDCSetting(ctx)
|
if errors.IsNotFoundErr(err) { // User is not onboarded, kickoff the onboard flow
|
||||||
if err != nil {
|
|
||||||
oc.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if u == nil {
|
|
||||||
// Recover the username from d.Username by default
|
// Recover the username from d.Username by default
|
||||||
username := info.Username
|
username := info.Username
|
||||||
|
|
||||||
// Fix blanks in username
|
// Fix blanks in username
|
||||||
username = strings.Replace(username, " ", "_", -1)
|
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 automatic onboard is enabled, skip the onboard page
|
||||||
if oidcSettings.AutoOnboard {
|
if oidcSettings.AutoOnboard {
|
||||||
log.Debug("Doing automatic onboarding\n")
|
log.Debug("Doing automatic onboarding\n")
|
||||||
@ -143,27 +136,31 @@ func (oc *OIDCController) Callback() {
|
|||||||
oidcSettings.UserClaim))
|
oidcSettings.UserClaim))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, onboarded := userOnboard(oc, info, username, tokenBytes)
|
userRec, onboarded := userOnboard(oc, info, username, tokenBytes)
|
||||||
if onboarded == false {
|
if onboarded == false {
|
||||||
log.Error("User not onboarded\n")
|
log.Error("User not onboarded\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("User automatically onboarded\n")
|
log.Debug("User automatically onboarded\n")
|
||||||
u = user
|
u = userRec
|
||||||
} else {
|
} else {
|
||||||
oc.SetSession(userInfoKey, string(ouDataStr))
|
oc.SetSession(userInfoKey, string(ouDataStr))
|
||||||
oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", username), http.StatusFound)
|
oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s", username), http.StatusFound)
|
||||||
// Once redirected, no further actions are done
|
// Once redirected, no further actions are done
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
oc.SendInternalServerError(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
oidc.InjectGroupsToUser(info, u)
|
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 {
|
if err != nil {
|
||||||
oc.SendInternalServerError(err)
|
oc.SendInternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, t, err := secretAndToken(tokenBytes)
|
_, t, err := secretAndToken(tokenBytes)
|
||||||
|
oidcUser := um.OIDCUserMeta
|
||||||
oidcUser.Token = t
|
oidcUser.Token = t
|
||||||
if err := dao.UpdateOIDCUser(oidcUser); err != nil {
|
if err := dao.UpdateOIDCUser(oidcUser); err != nil {
|
||||||
oc.SendInternalServerError(err)
|
oc.SendInternalServerError(err)
|
||||||
@ -171,7 +168,6 @@ func (oc *OIDCController) Callback() {
|
|||||||
}
|
}
|
||||||
oc.PopulateUserSession(*u)
|
oc.PopulateUserSession(*u)
|
||||||
oc.Controller.Redirect("/", http.StatusFound)
|
oc.Controller.Redirect("/", http.StatusFound)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func userOnboard(oc *OIDCController, info *oidc.UserInfo, username string, tokenBytes []byte) (*models.User, bool) {
|
func userOnboard(oc *OIDCController, info *oidc.UserInfo, username string, tokenBytes []byte) (*models.User, bool) {
|
||||||
|
@ -34,10 +34,10 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
common_http "github.com/goharbor/harbor/src/common/http"
|
common_http "github.com/goharbor/harbor/src/common/http"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"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/event/handler"
|
||||||
"github.com/goharbor/harbor/src/controller/health"
|
"github.com/goharbor/harbor/src/controller/health"
|
||||||
"github.com/goharbor/harbor/src/controller/registry"
|
"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/api"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/authproxy"
|
_ "github.com/goharbor/harbor/src/core/auth/authproxy"
|
||||||
_ "github.com/goharbor/harbor/src/core/auth/db"
|
_ "github.com/goharbor/harbor/src/core/auth/db"
|
||||||
@ -66,7 +66,7 @@ const (
|
|||||||
adminUserID = 1
|
adminUserID = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func updateInitPassword(userID int, password string) error {
|
func updateInitPassword(ctx context.Context, userID int, password string) error {
|
||||||
queryUser := models.User{UserID: userID}
|
queryUser := models.User{UserID: userID}
|
||||||
user, err := dao.GetUser(queryUser)
|
user, err := dao.GetUser(queryUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -76,11 +76,7 @@ func updateInitPassword(userID int, password string) error {
|
|||||||
return fmt.Errorf("user id: %d does not exist", userID)
|
return fmt.Errorf("user id: %d does not exist", userID)
|
||||||
}
|
}
|
||||||
if user.Salt == "" {
|
if user.Salt == "" {
|
||||||
salt := utils.GenerateRandomString()
|
err = ctluser.Ctl.UpdatePassword(ctx, userID, password)
|
||||||
|
|
||||||
user.Salt = salt
|
|
||||||
user.Password = password
|
|
||||||
err = dao.ChangeUserPassword(*user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
||||||
}
|
}
|
||||||
@ -203,7 +199,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to get admin's initial password: %v", err)
|
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)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ type MetaManager interface {
|
|||||||
Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error)
|
Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error)
|
||||||
// GetByUserID gets the oidc meta record by user's ID
|
// GetByUserID gets the oidc meta record by user's ID
|
||||||
GetByUserID(ctx context.Context, uid int) (*models.OIDCUser, error)
|
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 updates the cli secret of a user based on the user ID
|
||||||
SetCliSecretByUserID(ctx context.Context, uid int, secret string) error
|
SetCliSecretByUserID(ctx context.Context, uid int, secret string) error
|
||||||
}
|
}
|
||||||
@ -39,6 +41,21 @@ type metaManager struct {
|
|||||||
dao dao.MetaDAO
|
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) {
|
func (m *metaManager) Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error) {
|
||||||
return m.dao.Create(ctx, oidcUser)
|
return m.dao.Create(ctx, oidcUser)
|
||||||
}
|
}
|
||||||
|
@ -74,17 +74,43 @@ func (suite *DaoTestSuite) TestCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *DaoTestSuite) TestList() {
|
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.Nil(err)
|
||||||
suite.Len(users, 1)
|
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.Nil(err)
|
||||||
suite.Len(users, 1)
|
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() {
|
func (suite *DaoTestSuite) TestCreate() {
|
||||||
|
@ -35,7 +35,7 @@ var (
|
|||||||
type Manager interface {
|
type Manager interface {
|
||||||
// Get get user by user id
|
// Get get user by user id
|
||||||
Get(ctx context.Context, id int) (*models.User, error)
|
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)
|
GetByName(ctx context.Context, username string) (*models.User, error)
|
||||||
// List users according to the query
|
// List users according to the query
|
||||||
List(ctx context.Context, query *q.Query) (models.Users, error)
|
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 sets the system admin flag of the user in local DB
|
||||||
SetSysAdminFlag(ctx context.Context, id int, admin bool) error
|
SetSysAdminFlag(ctx context.Context, id int, admin bool) error
|
||||||
// UpdateProfile updates the user's profile
|
// 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 updates user's password
|
||||||
UpdatePassword(ctx context.Context, id int, newPassword string) error
|
UpdatePassword(ctx context.Context, id int, newPassword string) error
|
||||||
// VerifyLocalPassword verifies the password against the record in DB based on the input
|
// MatchLocalPassword tries to match the record in DB based on the input, the first return value is
|
||||||
VerifyLocalPassword(ctx context.Context, username, password string) (bool, error)
|
// 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
|
// 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")
|
return m.dao.Update(ctx, u, "username", "email", "deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) VerifyLocalPassword(ctx context.Context, username, password string) (bool, error) {
|
func (m *manager) MatchLocalPassword(ctx context.Context, usernameOrEmail, password string) (*models.User, error) {
|
||||||
u, err := m.GetByName(ctx, username)
|
l, err := m.dao.List(ctx, q.New(q.KeyWords{"username_or_email": usernameOrEmail}))
|
||||||
if err != nil {
|
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) {
|
func (m *manager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||||
return m.dao.Count(ctx, query)
|
return m.dao.Count(ctx, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) UpdateProfile(ctx context.Context, user *models.User) error {
|
func (m *manager) UpdateProfile(ctx context.Context, user *models.User, cols ...string) error {
|
||||||
return m.dao.Update(ctx, user, "email", "realname", "comment")
|
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 {
|
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
|
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) {
|
func (m *manager) GetByName(ctx context.Context, username string) (*models.User, error) {
|
||||||
users, err := m.dao.List(ctx, q.New(q.KeyWords{"username": username}))
|
users, err := m.dao.List(ctx, q.New(q.KeyWords{"username": username}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -15,15 +15,15 @@
|
|||||||
package security
|
package security
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/goharbor/harbor/src/lib/config"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"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"
|
||||||
"github.com/goharbor/harbor/src/common/security/local"
|
"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"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/pkg/oidc"
|
"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)
|
log.Warningf("failed to verify token: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
u, err := dao.GetUserBySubIss(claims.Subject, claims.Issuer)
|
u, err := user.Ctl.GetBySubIss(ctx, claims.Subject, claims.Issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("failed to get user based on token claims: %v", err)
|
log.Warningf("failed to get user based on token claims: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if u == nil {
|
|
||||||
log.Warning("user matches token's claims is not onboarded.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
setting, err := config.OIDCSetting(ctx)
|
setting, err := config.OIDCSetting(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get OIDC settings: %v", err)
|
log.Errorf("failed to get OIDC settings: %v", err)
|
||||||
|
@ -120,6 +120,29 @@ func (_m *Controller) GetByName(ctx context.Context, username string) (*models.U
|
|||||||
return r0, r1
|
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
|
// List provides a mock function with given fields: ctx, query
|
||||||
func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*models.User, error) {
|
func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*models.User, error) {
|
||||||
ret := _m.Called(ctx, query)
|
ret := _m.Called(ctx, query)
|
||||||
@ -185,13 +208,20 @@ func (_m *Controller) UpdatePassword(ctx context.Context, id int, password strin
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProfile provides a mock function with given fields: ctx, u
|
// UpdateProfile provides a mock function with given fields: ctx, u, cols
|
||||||
func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User) error {
|
func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User, cols ...string) error {
|
||||||
ret := _m.Called(ctx, u)
|
_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
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, *models.User) error); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok {
|
||||||
r0 = rf(ctx, u)
|
r0 = rf(ctx, u, cols...)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
@ -199,20 +229,20 @@ func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User) error {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyPassword provides a mock function with given fields: ctx, username, password
|
// VerifyPassword provides a mock function with given fields: ctx, usernameOrEmail, password
|
||||||
func (_m *Controller) VerifyPassword(ctx context.Context, username string, password string) (bool, error) {
|
func (_m *Controller) VerifyPassword(ctx context.Context, usernameOrEmail string, password string) (bool, error) {
|
||||||
ret := _m.Called(ctx, username, password)
|
ret := _m.Called(ctx, usernameOrEmail, password)
|
||||||
|
|
||||||
var r0 bool
|
var r0 bool
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok {
|
||||||
r0 = rf(ctx, username, password)
|
r0 = rf(ctx, usernameOrEmail, password)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Get(0).(bool)
|
r0 = ret.Get(0).(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
||||||
r1 = rf(ctx, username, password)
|
r1 = rf(ctx, usernameOrEmail, password)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,29 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) (usermodels.Users,
|
|||||||
return r0, r1
|
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
|
// SetSysAdminFlag provides a mock function with given fields: ctx, id, admin
|
||||||
func (_m *Manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) error {
|
func (_m *Manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) error {
|
||||||
ret := _m.Called(ctx, id, admin)
|
ret := _m.Called(ctx, id, admin)
|
||||||
@ -171,37 +194,23 @@ func (_m *Manager) UpdatePassword(ctx context.Context, id int, newPassword strin
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProfile provides a mock function with given fields: ctx, _a1
|
// UpdateProfile provides a mock function with given fields: ctx, _a1, col
|
||||||
func (_m *Manager) UpdateProfile(ctx context.Context, _a1 *models.User) error {
|
func (_m *Manager) UpdateProfile(ctx context.Context, _a1 *models.User, col ...string) error {
|
||||||
ret := _m.Called(ctx, _a1)
|
_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
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, *models.User) error); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok {
|
||||||
r0 = rf(ctx, _a1)
|
r0 = rf(ctx, _a1, col...)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r0
|
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
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user