code.gitea.io/gitea@v1.21.7/models/user/external_login_user.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package user 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 "code.gitea.io/gitea/models/db" 12 "code.gitea.io/gitea/modules/util" 13 14 "xorm.io/builder" 15 ) 16 17 // ErrExternalLoginUserAlreadyExist represents a "ExternalLoginUserAlreadyExist" kind of error. 18 type ErrExternalLoginUserAlreadyExist struct { 19 ExternalID string 20 UserID int64 21 LoginSourceID int64 22 } 23 24 // IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist. 25 func IsErrExternalLoginUserAlreadyExist(err error) bool { 26 _, ok := err.(ErrExternalLoginUserAlreadyExist) 27 return ok 28 } 29 30 func (err ErrExternalLoginUserAlreadyExist) Error() string { 31 return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID) 32 } 33 34 func (err ErrExternalLoginUserAlreadyExist) Unwrap() error { 35 return util.ErrAlreadyExist 36 } 37 38 // ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error. 39 type ErrExternalLoginUserNotExist struct { 40 UserID int64 41 LoginSourceID int64 42 } 43 44 // IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist. 45 func IsErrExternalLoginUserNotExist(err error) bool { 46 _, ok := err.(ErrExternalLoginUserNotExist) 47 return ok 48 } 49 50 func (err ErrExternalLoginUserNotExist) Error() string { 51 return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID) 52 } 53 54 func (err ErrExternalLoginUserNotExist) Unwrap() error { 55 return util.ErrNotExist 56 } 57 58 // ExternalLoginUser makes the connecting between some existing user and additional external login sources 59 type ExternalLoginUser struct { 60 ExternalID string `xorm:"pk NOT NULL"` 61 UserID int64 `xorm:"INDEX NOT NULL"` 62 LoginSourceID int64 `xorm:"pk NOT NULL"` 63 RawData map[string]any `xorm:"TEXT JSON"` 64 Provider string `xorm:"index VARCHAR(25)"` 65 Email string 66 Name string 67 FirstName string 68 LastName string 69 NickName string 70 Description string 71 AvatarURL string `xorm:"TEXT"` 72 Location string 73 AccessToken string `xorm:"TEXT"` 74 AccessTokenSecret string `xorm:"TEXT"` 75 RefreshToken string `xorm:"TEXT"` 76 ExpiresAt time.Time 77 } 78 79 type ExternalUserMigrated interface { 80 GetExternalName() string 81 GetExternalID() int64 82 } 83 84 type ExternalUserRemappable interface { 85 GetUserID() int64 86 RemapExternalUser(externalName string, externalID, userID int64) error 87 ExternalUserMigrated 88 } 89 90 func init() { 91 db.RegisterModel(new(ExternalLoginUser)) 92 } 93 94 // GetExternalLogin checks if a externalID in loginSourceID scope already exists 95 func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) { 96 return db.GetEngine(db.DefaultContext).Get(externalLoginUser) 97 } 98 99 // ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource 100 func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) { 101 externalAccounts := make([]*ExternalLoginUser, 0, 5) 102 err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID). 103 Desc("login_source_id"). 104 Find(&externalAccounts) 105 if err != nil { 106 return nil, err 107 } 108 109 return externalAccounts, nil 110 } 111 112 // LinkExternalToUser link the external user to the user 113 func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error { 114 has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). 115 NoAutoCondition(). 116 Exist(externalLoginUser) 117 if err != nil { 118 return err 119 } else if has { 120 return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID} 121 } 122 123 _, err = db.GetEngine(db.DefaultContext).Insert(externalLoginUser) 124 return err 125 } 126 127 // RemoveAccountLink will remove all external login sources for the given user 128 func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) { 129 deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID}) 130 if err != nil { 131 return deleted, err 132 } 133 if deleted < 1 { 134 return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID} 135 } 136 return deleted, err 137 } 138 139 // RemoveAllAccountLinks will remove all external login sources for the given user 140 func RemoveAllAccountLinks(ctx context.Context, user *User) error { 141 _, err := db.GetEngine(ctx).Delete(&ExternalLoginUser{UserID: user.ID}) 142 return err 143 } 144 145 // GetUserIDByExternalUserID get user id according to provider and userID 146 func GetUserIDByExternalUserID(provider, userID string) (int64, error) { 147 var id int64 148 _, err := db.GetEngine(db.DefaultContext).Table("external_login_user"). 149 Select("user_id"). 150 Where("provider=?", provider). 151 And("external_id=?", userID). 152 Get(&id) 153 if err != nil { 154 return 0, err 155 } 156 return id, nil 157 } 158 159 // UpdateExternalUserByExternalID updates an external user's information 160 func UpdateExternalUserByExternalID(external *ExternalLoginUser) error { 161 has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID). 162 NoAutoCondition(). 163 Exist(external) 164 if err != nil { 165 return err 166 } else if !has { 167 return ErrExternalLoginUserNotExist{external.UserID, external.LoginSourceID} 168 } 169 170 _, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).AllCols().Update(external) 171 return err 172 } 173 174 // FindExternalUserOptions represents an options to find external users 175 type FindExternalUserOptions struct { 176 Provider string 177 Limit int 178 Start int 179 } 180 181 func (opts FindExternalUserOptions) toConds() builder.Cond { 182 cond := builder.NewCond() 183 if len(opts.Provider) > 0 { 184 cond = cond.And(builder.Eq{"provider": opts.Provider}) 185 } 186 return cond 187 } 188 189 // FindExternalUsersByProvider represents external users via provider 190 func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) { 191 var users []ExternalLoginUser 192 err := db.GetEngine(db.DefaultContext).Where(opts.toConds()). 193 Limit(opts.Limit, opts.Start). 194 OrderBy("login_source_id ASC, external_id ASC"). 195 Find(&users) 196 if err != nil { 197 return nil, err 198 } 199 return users, nil 200 }