From dd136858f1ea40ad3c94191d647487fa4f31926c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.0. Signed-off-by: Daniel Baumann --- models/user/external_login_user.go | 219 +++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 models/user/external_login_user.go (limited to 'models/user/external_login_user.go') diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go new file mode 100644 index 0000000..0e764ef --- /dev/null +++ b/models/user/external_login_user.go @@ -0,0 +1,219 @@ +// 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]any `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(ctx context.Context, externalLoginUser *ExternalLoginUser) (bool, error) { + return db.GetEngine(ctx).Get(externalLoginUser) +} + +// LinkExternalToUser link the external user to the user +func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error { + has, err := db.Exist[ExternalLoginUser](ctx, builder.Eq{ + "external_id": externalLoginUser.ExternalID, + "login_source_id": externalLoginUser.LoginSourceID, + }) + if err != nil { + return err + } else if has { + return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID} + } + + _, err = db.GetEngine(ctx).Insert(externalLoginUser) + return err +} + +// RemoveAccountLink will remove all external login sources for the given user +func RemoveAccountLink(ctx context.Context, user *User, loginSourceID int64) (int64, error) { + deleted, err := db.GetEngine(ctx).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(ctx context.Context, provider, userID string) (int64, error) { + var id int64 + _, err := db.GetEngine(ctx).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(ctx context.Context, external *ExternalLoginUser) error { + has, err := db.Exist[ExternalLoginUser](ctx, builder.Eq{ + "external_id": external.ExternalID, + "login_source_id": external.LoginSourceID, + }) + if err != nil { + return err + } else if !has { + return ErrExternalLoginUserNotExist{external.UserID, external.LoginSourceID} + } + + _, err = db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).AllCols().Update(external) + return err +} + +// EnsureLinkExternalToUser link the external user to the user +func EnsureLinkExternalToUser(ctx context.Context, external *ExternalLoginUser) error { + has, err := db.Exist[ExternalLoginUser](ctx, builder.Eq{ + "external_id": external.ExternalID, + "login_source_id": external.LoginSourceID, + }) + if err != nil { + return err + } + + if has { + _, err = db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", external.ExternalID, external.LoginSourceID).AllCols().Update(external) + return err + } + + _, err = db.GetEngine(ctx).Insert(external) + return err +} + +// FindExternalUserOptions represents an options to find external users +type FindExternalUserOptions struct { + db.ListOptions + Provider string + UserID int64 + LoginSourceID int64 + HasRefreshToken bool + Expired bool + OrderBy string +} + +func (opts FindExternalUserOptions) ToConds() builder.Cond { + cond := builder.NewCond() + if len(opts.Provider) > 0 { + cond = cond.And(builder.Eq{"provider": opts.Provider}) + } + if opts.UserID > 0 { + cond = cond.And(builder.Eq{"user_id": opts.UserID}) + } + if opts.Expired { + cond = cond.And(builder.Lt{"expires_at": time.Now()}) + } + if opts.HasRefreshToken { + cond = cond.And(builder.Neq{"refresh_token": ""}) + } + if opts.LoginSourceID != 0 { + cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID}) + } + return cond +} + +func (opts FindExternalUserOptions) ToOrders() string { + return opts.OrderBy +} + +func IterateExternalLogin(ctx context.Context, opts FindExternalUserOptions, f func(ctx context.Context, u *ExternalLoginUser) error) error { + return db.Iterate(ctx, opts.ToConds(), f) +} -- cgit v1.2.3