mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-10-02 08:22:01 +00:00
Identify users using their remote ID (#1732)
This commit is contained in:
parent
02cfbc8cbf
commit
6d2240b2e6
14 changed files with 101 additions and 47 deletions
|
@ -65,7 +65,7 @@ func HandleAuth(c *gin.Context) {
|
||||||
config := ToConfig(c)
|
config := ToConfig(c)
|
||||||
|
|
||||||
// get the user from the database
|
// get the user from the database
|
||||||
u, err := _store.GetUserLogin(tmpuser.Login)
|
u, err := _store.GetUserRemoteID(tmpuser.ForgeRemoteID, tmpuser.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, types.RecordNotExist) {
|
if !errors.Is(err, types.RecordNotExist) {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
@ -93,6 +93,7 @@ func HandleAuth(c *gin.Context) {
|
||||||
// create the user account
|
// create the user account
|
||||||
u = &model.User{
|
u = &model.User{
|
||||||
Login: tmpuser.Login,
|
Login: tmpuser.Login,
|
||||||
|
ForgeRemoteID: tmpuser.ForgeRemoteID,
|
||||||
Token: tmpuser.Token,
|
Token: tmpuser.Token,
|
||||||
Secret: tmpuser.Secret,
|
Secret: tmpuser.Secret,
|
||||||
Email: tmpuser.Email,
|
Email: tmpuser.Email,
|
||||||
|
@ -115,6 +116,8 @@ func HandleAuth(c *gin.Context) {
|
||||||
u.Secret = tmpuser.Secret
|
u.Secret = tmpuser.Secret
|
||||||
u.Email = tmpuser.Email
|
u.Email = tmpuser.Email
|
||||||
u.Avatar = tmpuser.Avatar
|
u.Avatar = tmpuser.Avatar
|
||||||
|
u.ForgeRemoteID = tmpuser.ForgeRemoteID
|
||||||
|
u.Login = tmpuser.Login
|
||||||
u.Admin = u.Admin || config.IsAdmin(tmpuser)
|
u.Admin = u.Admin || config.IsAdmin(tmpuser)
|
||||||
|
|
||||||
// if self-registration is enabled for whitelisted organizations we need to
|
// if self-registration is enabled for whitelisted organizations we need to
|
||||||
|
|
|
@ -123,6 +123,7 @@ func convertUser(from *internal.Account, token *oauth2.Token) *model.User {
|
||||||
Secret: token.RefreshToken,
|
Secret: token.RefreshToken,
|
||||||
Expiry: token.Expiry.UTC().Unix(),
|
Expiry: token.Expiry.UTC().Unix(),
|
||||||
Avatar: from.Links.Avatar.Href,
|
Avatar: from.Links.Avatar.Href,
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(from.ID)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
Login string `json:"username"`
|
Login string `json:"username"`
|
||||||
Name string `json:"display_name"`
|
Name string `json:"display_name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
|
@ -125,6 +125,7 @@ func convertUser(from *internal.User, token *oauth.AccessToken) *model.User {
|
||||||
Token: token.Token,
|
Token: token.Token,
|
||||||
Email: from.EmailAddress,
|
Email: from.EmailAddress,
|
||||||
Avatar: avatarLink(from.EmailAddress),
|
Avatar: avatarLink(from.EmailAddress),
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(from.ID)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,29 +70,27 @@ func NewClientWithToken(ctx context.Context, url string, consumer *oauth.Consume
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FindCurrentUser() (*User, error) {
|
func (c *Client) FindCurrentUser() (*User, error) {
|
||||||
CurrentUserIDResponse, err := c.doGet(fmt.Sprintf(currentUserID, c.base))
|
currentUserIDResponse, err := c.doGet(fmt.Sprintf(currentUserID, c.base))
|
||||||
if CurrentUserIDResponse != nil {
|
|
||||||
defer CurrentUserIDResponse.Body.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer currentUserIDResponse.Body.Close()
|
||||||
|
|
||||||
bits, err := io.ReadAll(CurrentUserIDResponse.Body)
|
bits, err := io.ReadAll(currentUserIDResponse.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
login := string(bits)
|
login := string(bits)
|
||||||
|
|
||||||
CurrentUserResponse, err := c.doGet(fmt.Sprintf(pathUser, c.base, login))
|
currentUserResponse, err := c.doGet(fmt.Sprintf(pathUser, c.base, login))
|
||||||
if CurrentUserResponse != nil {
|
if currentUserResponse != nil {
|
||||||
defer CurrentUserResponse.Body.Close()
|
defer currentUserResponse.Body.Close()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
contents, err := io.ReadAll(CurrentUserResponse.Body)
|
contents, err := io.ReadAll(currentUserResponse.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,7 @@ func (c *Gitea) Login(ctx context.Context, w http.ResponseWriter, req *http.Requ
|
||||||
Expiry: token.Expiry.UTC().Unix(),
|
Expiry: token.Expiry.UTC().Unix(),
|
||||||
Login: account.UserName,
|
Login: account.UserName,
|
||||||
Email: account.Email,
|
Email: account.Email,
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(account.ID)),
|
||||||
Avatar: expandAvatar(c.URL, account.AvatarURL),
|
Avatar: expandAvatar(c.URL, account.AvatarURL),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,10 +129,11 @@ func (c *client) Login(ctx context.Context, res http.ResponseWriter, req *http.R
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.User{
|
return &model.User{
|
||||||
Login: *user.Login,
|
Login: user.GetLogin(),
|
||||||
Email: *email.Email,
|
Email: email.GetEmail(),
|
||||||
Token: token.AccessToken,
|
Token: token.AccessToken,
|
||||||
Avatar: *user.AvatarURL,
|
Avatar: user.GetAvatarURL(),
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(user.GetID())),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,7 @@ func (g *GitLab) Login(ctx context.Context, res http.ResponseWriter, req *http.R
|
||||||
Login: login.Username,
|
Login: login.Username,
|
||||||
Email: login.Email,
|
Email: login.Email,
|
||||||
Avatar: login.AvatarURL,
|
Avatar: login.AvatarURL,
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(login.ID)),
|
||||||
Token: token.AccessToken,
|
Token: token.AccessToken,
|
||||||
Secret: token.RefreshToken,
|
Secret: token.RefreshToken,
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package gogs
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -122,6 +123,7 @@ func (c *client) Login(_ context.Context, res http.ResponseWriter, req *http.Req
|
||||||
Login: account.UserName,
|
Login: account.UserName,
|
||||||
Email: account.Email,
|
Email: account.Email,
|
||||||
Avatar: expandAvatar(c.URL, account.AvatarUrl),
|
Avatar: expandAvatar(c.URL, account.AvatarUrl),
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(account.ID)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated by mockery v2.23.1. DO NOT EDIT.
|
// Code generated by mockery v2.26.1. DO NOT EDIT.
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ type User struct {
|
||||||
// required: true
|
// required: true
|
||||||
ID int64 `json:"id" xorm:"pk autoincr 'user_id'"`
|
ID int64 `json:"id" xorm:"pk autoincr 'user_id'"`
|
||||||
|
|
||||||
|
ForgeRemoteID ForgeRemoteID `json:"-" xorm:"forge_remote_id"`
|
||||||
|
|
||||||
// Login is the username for this user.
|
// Login is the username for this user.
|
||||||
//
|
//
|
||||||
// required: true
|
// required: true
|
||||||
|
|
|
@ -16,6 +16,7 @@ package datastore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s storage) GetUser(id int64) (*model.User, error) {
|
func (s storage) GetUser(id int64) (*model.User, error) {
|
||||||
|
@ -23,9 +24,23 @@ func (s storage) GetUser(id int64) (*model.User, error) {
|
||||||
return user, wrapGet(s.engine.ID(id).Get(user))
|
return user, wrapGet(s.engine.ID(id).Get(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storage) GetUserLogin(login string) (*model.User, error) {
|
func (s storage) GetUserRemoteID(remoteID model.ForgeRemoteID, login string) (*model.User, error) {
|
||||||
|
sess := s.engine.NewSession()
|
||||||
user := new(model.User)
|
user := new(model.User)
|
||||||
return user, wrapGet(s.engine.Where("user_login=?", login).Get(user))
|
err := wrapGet(sess.Where("forge_remote_id = ?", remoteID).Get(user))
|
||||||
|
if err != nil {
|
||||||
|
user, err = s.getUserLogin(sess, login)
|
||||||
|
}
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) GetUserLogin(login string) (*model.User, error) {
|
||||||
|
return s.getUserLogin(s.engine.NewSession(), login)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) getUserLogin(sess *xorm.Session, login string) (*model.User, error) {
|
||||||
|
user := new(model.User)
|
||||||
|
return user, wrapGet(sess.Where("user_login=?", login).Get(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storage) GetUserList(p *model.ListOptions) ([]*model.User, error) {
|
func (s storage) GetUserList(p *model.ListOptions) ([]*model.User, error) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated by mockery v2.23.1. DO NOT EDIT.
|
// Code generated by mockery v2.26.1. DO NOT EDIT.
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
|
@ -1079,6 +1079,32 @@ func (_m *Store) GetUserLogin(_a0 string) (*model.User, error) {
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserRemoteID provides a mock function with given fields: _a0, _a1
|
||||||
|
func (_m *Store) GetUserRemoteID(_a0 model.ForgeRemoteID, _a1 string) (*model.User, error) {
|
||||||
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
||||||
|
var r0 *model.User
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(model.ForgeRemoteID, string) (*model.User, error)); ok {
|
||||||
|
return rf(_a0, _a1)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(model.ForgeRemoteID, string) *model.User); ok {
|
||||||
|
r0 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*model.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(model.ForgeRemoteID, string) error); ok {
|
||||||
|
r1 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GlobalSecretFind provides a mock function with given fields: _a0
|
// GlobalSecretFind provides a mock function with given fields: _a0
|
||||||
func (_m *Store) GlobalSecretFind(_a0 string) (*model.Secret, error) {
|
func (_m *Store) GlobalSecretFind(_a0 string) (*model.Secret, error) {
|
||||||
ret := _m.Called(_a0)
|
ret := _m.Called(_a0)
|
||||||
|
|
|
@ -29,6 +29,8 @@ type Store interface {
|
||||||
// Users
|
// Users
|
||||||
// GetUser gets a user by unique ID.
|
// GetUser gets a user by unique ID.
|
||||||
GetUser(int64) (*model.User, error)
|
GetUser(int64) (*model.User, error)
|
||||||
|
// GetUserRemoteID gets a user by remote ID with fallback to login name.
|
||||||
|
GetUserRemoteID(model.ForgeRemoteID, string) (*model.User, error)
|
||||||
// GetUserLogin gets a user by unique Login name.
|
// GetUserLogin gets a user by unique Login name.
|
||||||
GetUserLogin(string) (*model.User, error)
|
GetUserLogin(string) (*model.User, error)
|
||||||
// GetUserList gets a list of all users in the system.
|
// GetUserList gets a list of all users in the system.
|
||||||
|
|
Loading…
Reference in a new issue