Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions apis/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,94 @@ func (e *User) Other(r *gin.RouterGroup) {
r.PUT("/user/:userID/password-reset", e.PasswordReset)
r.PUT("/user/userInfo", middleware.Auth.MiddlewareFunc(), e.UpdateUserInfo)
r.POST("/user/avatar", middleware.Auth.MiddlewareFunc(), e.UpdateAvatar)
r.GET("/user/oauth2", response.AuthHandler, e.GetOauth2)
r.POST("/user/binding", response.AuthHandler, e.Binding)
}

// Binding 绑定第三方登录
// @Summary 绑定第三方登录
// @Description 绑定第三方登录
// @Tags user
// @Accept application/json
// @Product application/json
// @Param data body models.UserLogin true "data"
// @Success 200
// @Router /admin/api/user/binding [post]
// @Security Bearer
func (e *User) Binding(ctx *gin.Context) {
api := response.Make(ctx)
verify := response.VerifyHandler(ctx)
if verify == nil {
api.Err(http.StatusForbidden)
return
}
req := &models.UserLogin{}
if api.Bind(req).Error != nil {
api.Err(http.StatusUnprocessableEntity)
return
}
var err error
user := verify.(*models.User)
user.Password = req.Password
userOAuth2 := &models.UserOAuth2{}
switch req.Provider {
case pkg.GithubLoginProvider:
userOAuth2, err = user.GetUserGithubOAuth2(ctx)
default:
api.Err(http.StatusNotImplemented)
return
}
if err != nil {
api.AddError(err).Log.Error("GetUserGithubOAuth2 error")
api.Err(http.StatusInternalServerError)
return
}
if userOAuth2.ID != "" {
api.OK(nil)
return
}
userOAuth2.User = nil
userOAuth2.UserID = verify.GetUserID()
err = center.GetDB(ctx, &models.UserOAuth2{}).Create(userOAuth2).Error
if err != nil {
api.AddError(err).Log.Error("CreateUserOAuth2 error")
api.Err(http.StatusInternalServerError)
return
}
api.OK(nil)
}

// GetOauth2 获取用户第三方登录信息
// @Summary 获取用户第三方登录信息
// @Description 获取用户第三方登录信息
// @Tags user
// @Accept application/json
// @Product application/json
// @Success 200 {object} []models.UserOAuth2
// @Router /admin/api/user/oauth2 [get]
// @Security Bearer
func (e *User) GetOauth2(ctx *gin.Context) {
api := response.Make(ctx)
verify := response.VerifyHandler(ctx)
if verify == nil {
api.Err(http.StatusForbidden)
return
}
user := &models.User{}
err := center.Default.GetDB(ctx, &models.User{}).
Preload("OAuth2").
Where("id = ?", verify.GetUserID()).
First(user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
api.Err(http.StatusNotFound)
return
}
api.AddError(err).Log.Error("GetUser error")
api.Err(http.StatusInternalServerError)
return
}
api.OK(user.OAuth2)
}

// ResetPassword 重置密码
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/nsqio/go-nsq v1.1.0
github.com/redis/go-redis/v9 v9.6.1
github.com/robfig/cron/v3 v3.0.1
github.com/sanity-io/litter v1.5.5
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/cast v1.7.0
github.com/spf13/cobra v1.8.1
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.3.2 h1:QhZu5AxQ+o1XZH0Ye05YzvJ0kAdK6VQc0z9NNMek7gc=
github.com/cyphar/filepath-securejoin v0.3.2/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand Down Expand Up @@ -628,6 +629,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -718,6 +720,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand Down
16 changes: 8 additions & 8 deletions models/tenant_migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {

tenantMenu := []Menu{
{
Name: "tenant",
Path: "/",
Name: "super-permission",
Path: "/super-permission",
Icon: "audit",
Sort: 16,
Type: pkg.DirectoryAccessType,
Expand Down Expand Up @@ -348,7 +348,7 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {
},
{
Name: "develop",
Path: "/",
Path: "/development-tools",
Icon: "tool",
Sort: 15,
Type: pkg.DirectoryAccessType,
Expand Down Expand Up @@ -396,7 +396,7 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {
menus := []Menu{
{
Name: "dashboard",
Path: "/",
Path: "/dashboard",
Icon: "smile",
Sort: 20,
Type: pkg.DirectoryAccessType,
Expand All @@ -417,7 +417,7 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {
},
{
Name: "system",
Path: "/",
Path: "/system",
Icon: "setting",
Sort: 19,
Type: pkg.DirectoryAccessType,
Expand Down Expand Up @@ -737,7 +737,7 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {
},
{
Name: "origination",
Path: "/",
Path: "/origination",
Icon: "apartment",
Sort: 18,
Type: pkg.DirectoryAccessType,
Expand Down Expand Up @@ -964,8 +964,8 @@ func (t *Tenant) Migrate(tenantImp center.TenantImp, tx *gorm.DB) error {
},
},
{
Name: "authority",
Path: "/",
Name: "permission",
Path: "/permission",
Icon: "safetyCertificate",
Sort: 17,
Type: pkg.DirectoryAccessType,
Expand Down
179 changes: 94 additions & 85 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/sanity-io/litter"
"github.com/spf13/cast"
"log/slog"
"net/http"
Expand Down Expand Up @@ -244,101 +245,36 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
_ = center.GetDB(ctx.(*gin.Context), &Role{}).Where(*defaultRole).First(defaultRole).Error
switch e.Provider {
case pkg.GithubLoginProvider:
// get user from github, then add user to db
// github user
clientID, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientId")
clientSecret, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientSecret")
redirectURL, _ := center.GetAppConfig().GetAppConfig(c, "security.githubRedirectUrl")
scope, _ := center.GetAppConfig().GetAppConfig(c, "security.githubScope")
scopes := strings.Split(scope, ",")
allowGroup, _ := center.GetAppConfig().GetAppConfig(c, "security.githubAllowGroup")
allowGroups := strings.Split(allowGroup, ",")
if len(allowGroup) == 0 {
allowGroups = nil
}
conf := &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopes,
RedirectURL: redirectURL,
Endpoint: oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
},
}
githubUser, err := pkg.GetUserFromGithub(ctx, conf, e.Password)
// get user from db
userOAuth2, err := e.GetUserGithubOAuth2(c)
if err != nil {
slog.Error("get user from github error", slog.Any("error", err))
return false, nil, err
}

if len(allowGroups) > 0 {
org, err := pkg.GetOrganizationsFromGithub(ctx, conf, e.Password)
if err != nil {
slog.Error("get organizations from github error", slog.Any("error", err))
return false, nil, err
}
if !pkg.InArray(allowGroups, org, "", 0) {
err = errors.New("user not in allow group")
slog.Error(err.Error())
return false, nil, err
}
}
// custom access func
if BeforeGithubVerify != nil {
err = BeforeGithubVerify(ctx, githubUser, e.Password)
if err != nil {
return false, nil, err
}
}

// get user from db
userOAuth2 := &UserOAuth2{}
err = center.GetDB(ctx.(*gin.Context), &UserOAuth2{}).Preload("User.Role").First(userOAuth2, "open_id = ?", fmt.Sprintf("%d", githubUser.ID)).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
slog.Error("get user from db error", slog.Any("error", err))
return false, nil, err
}
err = nil
userOAuth2 = &UserOAuth2{
OpenID: fmt.Sprintf("%d", githubUser.ID),
Sub: "github",
Name: githubUser.Login,
Email: githubUser.Email,
Profile: githubUser.Blog,
Picture: githubUser.AvatarURL,
NickName: githubUser.Login,
Website: githubUser.HTMLURL,
EmailVerified: true,
Locale: githubUser.Location,
Provider: pkg.GithubLoginProvider,
User: &User{
UserLogin: UserLogin{
RoleID: defaultRole.ID,
Username: githubUser.Email,
Email: githubUser.Email,
Password: e.Password,
Provider: pkg.GithubLoginProvider,
Status: enum.Enabled,
},
Name: githubUser.Login,
Avatar: githubUser.AvatarURL,
//Organization: githubUser.Company,
//Location: githubUser.Location,
//Introduction: githubUser.Bio,
Profile: githubUser.Blog,
//Verified: true,
//AccountID: fmt.Sprintf("%d", githubUser.ID),
if userOAuth2.ID == "" {
// register
userOAuth2.User = &User{
UserLogin: UserLogin{
RoleID: defaultRole.ID,
Username: userOAuth2.Email,
Email: userOAuth2.Email,
Password: e.Password,
Provider: pkg.GithubLoginProvider,
Status: enum.Enabled,
},
Name: userOAuth2.Name,
Avatar: userOAuth2.Picture,
Profile: userOAuth2.Profile,
}
// register user
err = center.GetDB(ctx.(*gin.Context), &User{}).Create(userOAuth2).Error
litter.Dump(e)
if e.GetUserID() != "" {
userOAuth2.User = nil
}
err = center.GetDB(c, &UserOAuth2{}).Create(userOAuth2).Error
if err != nil {
slog.Error("create user error", slog.Any("error", err))
return false, nil, err
}
userOAuth2.User.Role = defaultRole
}
return true, userOAuth2.User, nil
case pkg.LarkLoginProvider:
Expand Down Expand Up @@ -495,6 +431,79 @@ func (e *UserLogin) Verify(ctx context.Context) (bool, security.Verifier, error)
return verify == user.PasswordHash, user, nil
}

func (e *UserLogin) GetUserGithubOAuth2(c *gin.Context) (*UserOAuth2, error) {
clientID, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientId")
clientSecret, _ := center.GetAppConfig().GetAppConfig(c, "security.githubClientSecret")
redirectURL, _ := center.GetAppConfig().GetAppConfig(c, "security.githubRedirectUrl")
scope, _ := center.GetAppConfig().GetAppConfig(c, "security.githubScope")
scopes := strings.Split(scope, ",")
allowGroup, _ := center.GetAppConfig().GetAppConfig(c, "security.githubAllowGroup")
allowGroups := strings.Split(allowGroup, ",")
if len(allowGroup) == 0 {
allowGroups = nil
}
conf := &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopes,
RedirectURL: redirectURL,
Endpoint: oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
},
}
githubUser, err := pkg.GetUserFromGithub(c, conf, e.Password)
if err != nil {
slog.Error("get user from github error", slog.Any("error", err))
return nil, err
}
if len(allowGroups) > 0 {
org, err := pkg.GetOrganizationsFromGithub(c, conf, e.Password)
if err != nil {
slog.Error("get organizations from github error", slog.Any("error", err))
return nil, err
}
if !pkg.InArray(allowGroups, org, "", 0) {
err = errors.New("user not in allow group")
slog.Error(err.Error())
return nil, err

}
}
// custom access func
if BeforeGithubVerify != nil {
err = BeforeGithubVerify(c, githubUser, e.Password)
if err != nil {
return nil, err
}
}
// get user from db
userOAuth2 := &UserOAuth2{}
err = center.GetDB(c, &UserOAuth2{}).Preload("User.Role").First(userOAuth2, "open_id = ?", fmt.Sprintf("%d", githubUser.ID)).Error
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
slog.Error("get user from db error", slog.Any("error", err))
return nil, err
}
err = nil
userOAuth2 = &UserOAuth2{
UserID: e.GetUserID(),
OpenID: fmt.Sprintf("%d", githubUser.ID),
Sub: "github",
Name: githubUser.Login,
Email: githubUser.Email,
Profile: githubUser.Blog,
Picture: githubUser.AvatarURL,
NickName: githubUser.Login,
Website: githubUser.HTMLURL,
EmailVerified: true,
Locale: githubUser.Location,
Provider: pkg.GithubLoginProvider,
}
}
return userOAuth2, nil
}

func (e *UserLogin) GetDepartmentUserID(tx *gorm.DB) []string {
ids := make([]string, 0)
tx.Model(&User{}).
Expand Down