You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

external_login_user.go 6.2KB

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