You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
forgejo/models/user/external_login_user.go

201 lines
6.3 KiB
Go

// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"context"
"fmt"
"time"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
// ErrExternalLoginUserAlreadyExist represents a "ExternalLoginUserAlreadyExist" kind of error.
type ErrExternalLoginUserAlreadyExist struct {
ExternalID string
UserID int64
LoginSourceID int64
}
// IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist.
func IsErrExternalLoginUserAlreadyExist(err error) bool {
_, ok := err.(ErrExternalLoginUserAlreadyExist)
return ok
}
func (err ErrExternalLoginUserAlreadyExist) Error() string {
return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID)
}
func (err ErrExternalLoginUserAlreadyExist) Unwrap() error {
return util.ErrAlreadyExist
}
// ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error.
type ErrExternalLoginUserNotExist struct {
UserID int64
LoginSourceID int64
}
// IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist.
func IsErrExternalLoginUserNotExist(err error) bool {
_, ok := err.(ErrExternalLoginUserNotExist)
return ok
}
func (err ErrExternalLoginUserNotExist) Error() string {
return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID)
}
func (err ErrExternalLoginUserNotExist) Unwrap() error {
return util.ErrNotExist
}
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
type ExternalLoginUser struct {
ExternalID string `xorm:"pk NOT NULL"`
UserID int64 `xorm:"INDEX NOT NULL"`
LoginSourceID int64 `xorm:"pk NOT NULL"`
RawData map[string]interface{} `xorm:"TEXT JSON"`
Provider string `xorm:"index VARCHAR(25)"`
Email string
Name string
FirstName string
LastName string
NickName string
Description string
AvatarURL string `xorm:"TEXT"`
Location string
AccessToken string `xorm:"TEXT"`
AccessTokenSecret string `xorm:"TEXT"`
RefreshToken string `xorm:"TEXT"`
ExpiresAt time.Time
}
type ExternalUserMigrated interface {
GetExternalName() string
GetExternalID() int64
}
type ExternalUserRemappable interface {
GetUserID() int64
RemapExternalUser(externalName string, externalID, userID int64) error
ExternalUserMigrated
}
func init() {
db.RegisterModel(new(ExternalLoginUser))
}
// GetExternalLogin checks if a externalID in loginSourceID scope already exists
func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) {
return db.GetEngine(db.DefaultContext).Get(externalLoginUser)
}
// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
externalAccounts := make([]*ExternalLoginUser, 0, 5)
err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID).
Desc("login_source_id").
Find(&externalAccounts)
if err != nil {
return nil, err
}
return externalAccounts, nil
}
// LinkExternalToUser link the external user to the user
func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error {
has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
NoAutoCondition().
Exist(externalLoginUser)
if err != nil {
return err
} else if has {
return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
}
_, err = db.GetEngine(db.DefaultContext).Insert(externalLoginUser)
return err
}
// RemoveAccountLink will remove all external login sources for the given user
func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) {
deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID})
if err != nil {
return deleted, err
}
if deleted < 1 {
return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID}
}
return deleted, err
}
// RemoveAllAccountLinks will remove all external login sources for the given user
func RemoveAllAccountLinks(ctx context.Context, user *User) error {
_, err := db.GetEngine(ctx).Delete(&ExternalLoginUser{UserID: user.ID})
return err
}
// GetUserIDByExternalUserID get user id according to provider and userID
func GetUserIDByExternalUserID(provider, userID string) (int64, error) {
var id int64
_, err := db.GetEngine(db.DefaultContext).Table("external_login_user").
Select("user_id").
Where("provider=?", provider).
And("external_id=?", userID).
Get(&id)
if err != nil {
return 0, err
}
return id, nil
}
// UpdateExternalUserByExternalID updates an external user's information
func UpdateExternalUserByExternalID(external *ExternalLoginUser) error {
has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).
NoAutoCondition().
Exist(external)
if err != nil {
return err
} else if !has {
return ErrExternalLoginUserNotExist{external.UserID, external.LoginSourceID}
}
_, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).AllCols().Update(external)
return err
}
// FindExternalUserOptions represents an options to find external users
type FindExternalUserOptions struct {
Provider string
Limit int
Start int
}
func (opts FindExternalUserOptions) toConds() builder.Cond {
cond := builder.NewCond()
if len(opts.Provider) > 0 {
cond = cond.And(builder.Eq{"provider": opts.Provider})
}
return cond
}
// FindExternalUsersByProvider represents external users via provider
func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
var users []ExternalLoginUser
err := db.GetEngine(db.DefaultContext).Where(opts.toConds()).
Limit(opts.Limit, opts.Start).
OrderBy("login_source_id ASC, external_id ASC").
Find(&users)
if err != nil {
return nil, err
}
return users, nil
}