Skip to content

Commit

Permalink
chore: email lowercase handling (#5196)
Browse files Browse the repository at this point in the history
* email lowercase handling

* removing request email from update call

* authenticator update

* adding argocd assets

* converting email to lower cse

* updating authorizer

* adding argocd assets

* removing email from auth verification

* authenticator lib update
  • Loading branch information
iamayushm authored Jun 7, 2024
1 parent 530088a commit 9feb81d
Show file tree
Hide file tree
Showing 27 changed files with 163 additions and 94 deletions.
5 changes: 3 additions & 2 deletions api/auth/user/UserAuthHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func (handler UserAuthHandlerImpl) AddDefaultPolicyAndRoles(w http.ResponseWrite

}
func (handler UserAuthHandlerImpl) AuthVerification(w http.ResponseWriter, r *http.Request) {
verified, err := handler.userAuthService.AuthVerification(r)
verified, _, err := handler.userAuthService.AuthVerification(r)
if err != nil {
handler.logger.Errorw("service err, AuthVerification", "err", err)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
Expand All @@ -253,13 +253,14 @@ func (handler UserAuthHandlerImpl) AuthVerificationV2(w http.ResponseWriter, r *
isSuperAdmin = true
}
response := make(map[string]interface{})
verified, err := handler.userAuthService.AuthVerification(r)
verified, emailId, err := handler.userAuthService.AuthVerification(r)
if err != nil {
handler.logger.Errorw("service err, AuthVerification", "err", err)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
response["isSuperAdmin"] = isSuperAdmin
response["isVerified"] = verified
response["emailId"] = emailId
common.WriteJsonResp(w, nil, response, http.StatusOK)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.8.0
github.com/devtron-labs/authenticator v0.4.35-0.20240405091826-a91813c53470
github.com/devtron-labs/authenticator v0.4.35-0.20240607135426-c86e868ecee1
github.com/devtron-labs/common-lib v0.0.19-0.20240607054959-82c79c23b046
github.com/devtron-labs/protos v0.0.3-0.20240326053929-48e42d9d4534
github.com/evanphx/json-patch v5.6.0+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsP
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/devtron-labs/authenticator v0.4.35-0.20240405091826-a91813c53470 h1:AUTYcDnL6w6Ux+264VldYaOUQAP6pDZ5Tq8wCKJyiEg=
github.com/devtron-labs/authenticator v0.4.35-0.20240405091826-a91813c53470/go.mod h1:JQxTCMmQisrpjzETJr0tzVadV+wW23rHEZAY7JVyK3s=
github.com/devtron-labs/authenticator v0.4.35-0.20240607135426-c86e868ecee1 h1:qdkpTAo2Kr0ZicZIVXfNwsGSshpc9OB9j9RzmKYdIwY=
github.com/devtron-labs/authenticator v0.4.35-0.20240607135426-c86e868ecee1/go.mod h1:IkKPPEfgLCMR29he5yv2OCC6iM2R7K5/0AA3k8b9XNc=
github.com/devtron-labs/common-lib v0.0.19-0.20240607054959-82c79c23b046 h1:hOyqkgILg+eDttLV6X7OAAo9PKEHzInUmBTVy/EY/iI=
github.com/devtron-labs/common-lib v0.0.19-0.20240607054959-82c79c23b046/go.mod h1:deAcJ5IjUjM6ozZQLJEgPWDUA0mKa632LBsKx8uM9TE=
github.com/devtron-labs/protos v0.0.3-0.20240326053929-48e42d9d4534 h1:TElPRU69QedW7DIQiiQxtjwSQ6cK0fCTAMGvSLhP0ac=
Expand Down
8 changes: 5 additions & 3 deletions internal/sql/repository/UserAttributesRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (repo UserAttributesRepositoryImpl) UpdateDataValByKey(attrDto *UserAttribu
if err != nil {
return err
}
query := "update user_attributes SET user_data = user_data::jsonb - ? || ? where email_id = ?"
query := "update user_attributes SET user_data = user_data::jsonb - ? || ? where email_id ilike ?"

_, err = repo.dbConnection.
Query(userAttr, query, attrDto.Key, string(updatedValJson), attrDto.EmailId)
Expand All @@ -97,7 +97,8 @@ func (repo UserAttributesRepositoryImpl) UpdateDataValByKey(attrDto *UserAttribu

func (repo UserAttributesRepositoryImpl) GetDataValueByKey(attrDto *UserAttributesDao) (string, error) {
model := &UserAttributes{}
err := repo.dbConnection.Model(model).Where("email_id = ?", attrDto.EmailId).
err := repo.dbConnection.Model(model).Where("email_id ilike ?", attrDto.EmailId).
Limit(1).
Select()
if err != nil {
return "", err
Expand All @@ -118,7 +119,8 @@ func (repo UserAttributesRepositoryImpl) GetDataValueByKey(attrDto *UserAttribut

func (repo UserAttributesRepositoryImpl) GetUserDataByEmailId(emailId string) (string, error) {
model := &UserAttributes{}
err := repo.dbConnection.Model(model).Where("email_id = ?", emailId).
err := repo.dbConnection.Model(model).Where("email_id ilike ?", emailId).
Limit(1).
Select()
if err != nil {
return "", err
Expand Down
28 changes: 16 additions & 12 deletions pkg/auth/user/UserAuthService.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/json"
"errors"
"fmt"
util2 "github.com/devtron-labs/devtron/pkg/auth/user/util"
"log"
"math/rand"
"net/http"
Expand Down Expand Up @@ -53,13 +54,13 @@ type UserAuthService interface {
HandleRefresh(w http.ResponseWriter, r *http.Request)

CreateRole(roleData *bean.RoleData) (bool, error)
AuthVerification(r *http.Request) (bool, error)
AuthVerification(r *http.Request) (bool, string, error)
DeleteRoles(entityType string, entityName string, tx *pg.Tx, envIdentifier string, workflowName string) error
}

type UserAuthServiceImpl struct {
userAuthRepository repository.UserAuthRepository
//sessionClient is being used for argocd username-password login proxy
// sessionClient is being used for argocd username-password login proxy
sessionClient session2.ServiceClient
logger *zap.SugaredLogger
userRepository repository.UserRepository
Expand All @@ -71,7 +72,7 @@ type UserAuthServiceImpl struct {
var (
cStore *sessions.CookieStore
dexOauthConfig *oauth2.Config
//googleOauthConfig *oauth2.Config
// googleOauthConfig *oauth2.Config
oauthStateString = randToken()
idTokenVerifier *oidc.IDTokenVerifier
jwtKey = randKey()
Expand Down Expand Up @@ -202,6 +203,7 @@ func (impl UserAuthServiceImpl) HandleRefresh(w http.ResponseWriter, r *http.Req
writeResponse(http.StatusBadRequest, "StatusBadRequest", w, errors.New("StatusBadRequest"))
return
}
claims.Email = util2.ConvertEmailToLowerCase(claims.Email)
bearerToken := claims.Token
user, err := authorize(context.Background(), bearerToken)
if err != nil {
Expand Down Expand Up @@ -257,11 +259,12 @@ func (impl UserAuthServiceImpl) HandleRefresh(w http.ResponseWriter, r *http.Req
}

func (impl UserAuthServiceImpl) HandleLoginWithClientIp(ctx context.Context, username, password, clientIp string) (string, error) {
impl.logger.Info("login with client ip")
token, err := impl.HandleLogin(username, password)
if err == nil {
id, _, err := impl.userService.GetUserByToken(ctx, token)
if err != nil {
impl.logger.Infow("error occured while getting user by token", "err", err)
impl.logger.Errorw("error occurred while getting user by token", "err", err)
} else {
impl.userService.SaveLoginAudit("", clientIp, id)
}
Expand Down Expand Up @@ -308,14 +311,15 @@ func (impl UserAuthServiceImpl) HandleDexCallback(w http.ResponseWriter, r *http
// Rollback tx on error.
defer tx.Rollback()

Claims.Email = util2.ConvertEmailToLowerCase(Claims.Email)
dbUser, err := impl.userRepository.FetchUserDetailByEmail(Claims.Email)
if err != nil {
impl.logger.Errorw("Exception while fetching user from db", "err", err)
}
if dbUser.Id > 0 {
// Do nothing, User already exist in our db. (unique check by email id)
} else {
//create new user in our db on d basis of info got from google api or hex. assign a basic role
// create new user in our db on d basis of info got from google api or hex. assign a basic role
model := &repository.UserModel{
EmailId: Claims.Email,
AccessToken: rawIDToken,
Expand Down Expand Up @@ -449,7 +453,7 @@ func (impl UserAuthServiceImpl) CreateRole(roleData *bean.RoleData) (bool, error
return true, nil
}

func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, error) {
func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, string, error) {
token := r.Header.Get("token")
if token == "" {
impl.logger.Infow("no token provided")
Expand All @@ -458,7 +462,7 @@ func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, error)
Code: constants.UserNoTokenProvided,
InternalMessage: "no token provided",
}
return false, err
return false, "", err
}

_, err := impl.sessionManager.VerifyToken(token)
Expand All @@ -470,12 +474,12 @@ func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, error)
InternalMessage: "failed to verify token",
UserMessage: "token verification failed while getting logged in user",
}
return false, err
return false, "", err
}
emailId, version, err := impl.userService.GetEmailAndVersionFromToken(token)
if err != nil {
impl.logger.Errorw("AuthVerification failed ", "error", err)
return false, err
return false, "", err
}
exists := impl.userService.UserExists(emailId)
if !exists {
Expand All @@ -485,7 +489,7 @@ func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, error)
InternalMessage: "user does not exist",
UserMessage: "active user does not exist",
}
return false, err
return false, "", err
}
// checking length of version, to ensure backward compatibility as earlier we did not
// have version for api-tokens
Expand All @@ -494,12 +498,12 @@ func (impl UserAuthServiceImpl) AuthVerification(r *http.Request) (bool, error)
err := impl.userService.CheckIfTokenIsValid(emailId, version)
if err != nil {
impl.logger.Errorw("token is not valid", "error", err, "token", token)
return false, err
return false, "", err
}
}

//TODO - extends for other purpose
return true, nil
return true, emailId, nil
}

func (impl UserAuthServiceImpl) DeleteRoles(entityType string, entityName string, tx *pg.Tx, envIdentifier string, workflowName string) (err error) {
Expand Down
3 changes: 2 additions & 1 deletion pkg/auth/user/UserService.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/devtron-labs/devtron/pkg/auth/user/adapter"
userHelper "github.com/devtron-labs/devtron/pkg/auth/user/helper"
"github.com/devtron-labs/devtron/pkg/auth/user/repository/helper"
util3 "github.com/devtron-labs/devtron/pkg/auth/user/util"
"net/http"
"strconv"
"strings"
Expand Down Expand Up @@ -1364,7 +1365,7 @@ func (impl *UserServiceImpl) GetEmailAndVersionFromToken(token string) (string,
email = "admin"
}

return email, tokenVersion, nil
return util3.ConvertEmailToLowerCase(email), tokenVersion, nil
}

func (impl *UserServiceImpl) GetByIds(ids []int32) ([]bean.UserInfo, error) {
Expand Down
60 changes: 41 additions & 19 deletions pkg/auth/user/repository/UserRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
package repository

import (
"fmt"
"github.com/devtron-labs/devtron/api/bean"
userBean "github.com/devtron-labs/devtron/pkg/auth/user/bean"
"github.com/devtron-labs/devtron/pkg/auth/user/repository/helper"
"github.com/devtron-labs/devtron/pkg/auth/user/util"
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
"go.uber.org/zap"
Expand Down Expand Up @@ -59,13 +62,14 @@ func NewUserRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger) *User
}

type UserModel struct {
TableName struct{} `sql:"users" pg:",discard_unknown_columns"`
Id int32 `sql:"id,pk"`
EmailId string `sql:"email_id,notnull"`
AccessToken string `sql:"access_token"`
Active bool `sql:"active,notnull"`
UserType string `sql:"user_type"`
UserAudit *UserAudit `sql:"-"`
TableName struct{} `sql:"users" pg:",discard_unknown_columns"`
Id int32 `sql:"id,pk"`
EmailId string `sql:"email_id,notnull"`
RequestEmailId string `sql:"request_email_id"`
AccessToken string `sql:"access_token"`
Active bool `sql:"active,notnull"`
UserType string `sql:"user_type"`
UserAudit *UserAudit `sql:"-"`
sql.AuditLog
}

Expand All @@ -79,6 +83,8 @@ type UserRoleModel struct {
}

func (impl UserRepositoryImpl) CreateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) {
userModel.RequestEmailId = userModel.EmailId
userModel.EmailId = util.ConvertEmailToLowerCase(userModel.EmailId)
err := tx.Insert(userModel)
if err != nil {
impl.Logger.Error(err)
Expand All @@ -88,6 +94,7 @@ func (impl UserRepositoryImpl) CreateUser(userModel *UserModel, tx *pg.Tx) (*Use
return userModel, nil
}
func (impl UserRepositoryImpl) UpdateUser(userModel *UserModel, tx *pg.Tx) (*UserModel, error) {
userModel.EmailId = util.ConvertEmailToLowerCase(userModel.EmailId)
err := tx.Update(userModel)
if err != nil {
impl.Logger.Error(err)
Expand Down Expand Up @@ -117,6 +124,7 @@ func (impl UserRepositoryImpl) UpdateToInactiveByIds(ids []int32, tx *pg.Tx, log
func (impl UserRepositoryImpl) GetById(id int32) (*UserModel, error) {
var model UserModel
err := impl.dbConnection.Model(&model).Where("id = ?", id).Where("active = ?", true).Select()
model.EmailId = util.ConvertEmailToLowerCase(model.EmailId)
return &model, err
}

Expand All @@ -134,13 +142,14 @@ func (impl UserRepositoryImpl) GetEmailByIds(ids []int32) ([]string, error) {
for _, model := range models {
userEmails = append(userEmails, model.EmailId)
}
return userEmails, err
return util.ConvertEmailsToLowerCase(userEmails), err

}

func (impl UserRepositoryImpl) GetByIdIncludeDeleted(id int32) (*UserModel, error) {
var model UserModel
err := impl.dbConnection.Model(&model).Where("id = ?", id).Select()
model.EmailId = util.ConvertEmailToLowerCase(model.EmailId)
return &model, err
}

Expand All @@ -150,6 +159,9 @@ func (impl UserRepositoryImpl) GetAllExcludingApiTokenUser() ([]UserModel, error
Where("active = ?", true).
Where("user_type is NULL or user_type != ?", bean.USER_TYPE_API_TOKEN).
Order("updated_on desc").Select()
for i, user := range userModel {
userModel[i].EmailId = util.ConvertEmailToLowerCase(user.EmailId)
}
return userModel, err
}

Expand All @@ -160,20 +172,23 @@ func (impl UserRepositoryImpl) GetAllExecutingQuery(query string) ([]UserModel,
impl.Logger.Error("error in GetAllExecutingQuery", "err", err, "query", query)
return nil, err
}
for i, user := range userModel {
userModel[i].EmailId = util.ConvertEmailToLowerCase(user.EmailId)
}
return userModel, err
}

func (impl UserRepositoryImpl) FetchActiveUserByEmail(email string) (bean.UserInfo, error) {
var users bean.UserInfo

query := "SELECT u.id, u.email_id, u.access_token, u.user_type FROM users u " +
"WHERE u.active = true and u.email_id ILIKE ? order by u.updated_on desc"
query := fmt.Sprintf("SELECT u.id, u.email_id, u.access_token, u.user_type FROM users u"+
" WHERE u.active = true and %s order by u.updated_on desc", helper.GetEmailSearchQuery("u", email))
_, err := impl.dbConnection.Query(&users, query, email)
if err != nil {
impl.Logger.Error("Exception caught:", err)
impl.Logger.Errorw("Exception caught:", "err", err)
return users, err
}

users.EmailId = util.ConvertEmailToLowerCase(email)
return users, nil
}

Expand All @@ -182,11 +197,11 @@ func (impl UserRepositoryImpl) FetchUserDetailByEmail(email string) (bean.UserIn
var users []bean.UserRole
var userFinal bean.UserInfo

query := "SELECT u.id, u.email_id, u.user_type, r.role FROM users u" +
" INNER JOIN user_roles ur ON ur.user_id=u.id" +
" INNER JOIN roles r ON r.id=ur.role_id" +
" WHERE u.email_id= ? and u.active = true" +
" ORDER BY u.updated_on desc;"
query := fmt.Sprintf("SELECT u.id, u.email_id, u.user_type, r.role FROM users u"+
" INNER JOIN user_roles ur ON ur.user_id=u.id"+
" INNER JOIN roles r ON r.id=ur.role_id"+
" WHERE %s and u.active = true"+
" ORDER BY u.updated_on desc;", helper.GetEmailSearchQuery("u", email))
_, err := impl.dbConnection.Query(&users, query, email)
if err != nil {
return userFinal, err
Expand All @@ -196,7 +211,7 @@ func (impl UserRepositoryImpl) FetchUserDetailByEmail(email string) (bean.UserIn
for _, item := range users {
userFinal.Exist = true
userFinal.Id = item.Id
userFinal.EmailId = item.EmailId
userFinal.EmailId = util.ConvertEmailToLowerCase(item.EmailId)
role = append(role, item.Role)
}
userFinal.Roles = role
Expand All @@ -205,6 +220,9 @@ func (impl UserRepositoryImpl) FetchUserDetailByEmail(email string) (bean.UserIn
func (impl UserRepositoryImpl) GetByIds(ids []int32) ([]UserModel, error) {
var model []UserModel
err := impl.dbConnection.Model(&model).Where("id in (?)", pg.In(ids)).Where("active = ?", true).Select()
for i, m := range model {
model[i].EmailId = util.ConvertEmailToLowerCase(m.EmailId)
}
return model, err
}

Expand All @@ -215,15 +233,19 @@ func (impl *UserRepositoryImpl) GetConnection() (dbConnection *pg.DB) {
func (impl UserRepositoryImpl) FetchUserMatchesByEmailIdExcludingApiTokenUser(email string) ([]UserModel, error) {
var model []UserModel
err := impl.dbConnection.Model(&model).
Where("email_id like (?)", "%"+email+"%").
Where("email_id ilike (?)", "%"+email+"%").
Where("user_type is NULL or user_type != ?", bean.USER_TYPE_API_TOKEN).
Where("active = ?", true).Select()
for i, m := range model {
model[i].EmailId = util.ConvertEmailToLowerCase(m.EmailId)
}
return model, err
}

func (impl UserRepositoryImpl) FetchActiveOrDeletedUserByEmail(email string) (*UserModel, error) {
var model UserModel
err := impl.dbConnection.Model(&model).Where("email_id ILIKE (?)", email).Limit(1).Select()
model.EmailId = util.ConvertEmailToLowerCase(email)
return &model, err
}

Expand Down
Loading

0 comments on commit 9feb81d

Please sign in to comment.