Thanks to visit codestin.com
Credit goes to github.com

Skip to content

WIP: Add Gitea Login support #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 19, 2019
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
4 changes: 4 additions & 0 deletions config/env.sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ content = 发票,共产党
client_id = xxx
client_secret = xxx

[gitea]
client_id = xxx
client_secret = xxx

[account]
; 是否验证邮箱
verify_email = 0
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/studygolang/studygolang
go 1.12

require (
code.gitea.io/sdk/gitea v0.0.0-20191106151626-e4082d89cc3b
github.com/PuerkitoBio/goquery v1.5.0
github.com/Unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e // indirect
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
code.gitea.io/sdk v0.0.0-20191106151626-e4082d89cc3b h1:bAdeOAgzWZ2R8uMTiq4/K0ViBl/j8XGOEok+DciPN9Y=
code.gitea.io/sdk/gitea v0.0.0-20191106151626-e4082d89cc3b h1:T26uiLOnyGHLGvE1+as/j97ceSHk5gt9NgAMaBf/BZw=
code.gitea.io/sdk/gitea v0.0.0-20191106151626-e4082d89cc3b/go.mod h1:8IxkM1gyiwEjfO0m47bcmr3u3foR15+LoVub43hCHd0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
Expand Down
46 changes: 46 additions & 0 deletions http/controller/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type OAuthController struct{}
func (self OAuthController) RegisterRoute(g *echo.Group) {
g.GET("/oauth/github/callback", self.GithubCallback)
g.GET("/oauth/github/login", self.GithubLogin)

g.GET("/oauth/gitea/callback", self.GiteaCallback)
g.GET("/oauth/gitea/login", self.GiteaLogin)
}

func (OAuthController) GithubLogin(ctx echo.Context) error {
Expand Down Expand Up @@ -67,3 +70,46 @@ func (OAuthController) GithubCallback(ctx echo.Context) error {

return ctx.Redirect(http.StatusSeeOther, "/")
}

func (OAuthController) GiteaLogin(ctx echo.Context) error {
uri := ctx.QueryParam("uri")
url := logic.DefaultThirdUser.GiteaAuthCodeUrl(context.EchoContext(ctx), uri)
return ctx.Redirect(http.StatusSeeOther, url)
}

func (OAuthController) GiteaCallback(ctx echo.Context) error {
code := ctx.FormValue("code")

me, ok := ctx.Get("user").(*model.Me)
if ok {
// 已登录用户,绑定 github
logic.DefaultThirdUser.BindGitea(context.EchoContext(ctx), code, me)

redirectURL := ctx.QueryParam("redirect_url")
if redirectURL == "" {
redirectURL = "/account/edit#connection"
}
return ctx.Redirect(http.StatusSeeOther, redirectURL)
}

user, err := logic.DefaultThirdUser.LoginFromGitea(context.EchoContext(ctx), code)
if err != nil || user.Uid == 0 {
var errMsg = ""
if err != nil {
errMsg = err.Error()
} else {
errMsg = "服务内部错误"
}

return render(ctx, "login.html", map[string]interface{}{"error": errMsg})
}

// 登录成功,种cookie
SetLoginCookie(ctx, user.Username)

if user.Balance == 0 {
return ctx.Redirect(http.StatusSeeOther, "/balance")
}

return ctx.Redirect(http.StatusSeeOther, "/")
}
207 changes: 205 additions & 2 deletions logic/third_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ package logic
import (
"encoding/json"
"errors"
"io/ioutil"

. "github.com/studygolang/studygolang/db"
"github.com/studygolang/studygolang/model"
"io/ioutil"

"github.com/polaris1119/logger"

Expand All @@ -21,8 +22,10 @@ import (
)

var githubConf *oauth2.Config
var giteaConf *oauth2.Config

const GithubAPIBaseUrl = "https://api.github.com"
const GiteaAPIBaseUrl = "https://gitea.com/api/v1"

func init() {
githubConf = &oauth2.Config{
Expand All @@ -34,6 +37,15 @@ func init() {
TokenURL: "https://github.com/login/oauth/access_token",
},
}

giteaConf = &oauth2.Config{
ClientID: config.ConfigFile.MustValue("gitea", "client_id"),
ClientSecret: config.ConfigFile.MustValue("gitea", "client_secret"),
Endpoint: oauth2.Endpoint{
AuthURL: "https://gitea.com/login/oauth/authorize",
TokenURL: "https://gitea.com/login/oauth/access_token",
},
}
}

type ThirdUserLogic struct{}
Expand Down Expand Up @@ -200,6 +212,166 @@ func (self ThirdUserLogic) BindGithub(ctx context.Context, code string, me *mode
return nil
}

func (ThirdUserLogic) GiteaAuthCodeUrl(ctx context.Context, redirectURL string) string {
// Redirect user to consent page to ask for permission
// for the scopes specified above.
giteaConf.RedirectURL = redirectURL
return giteaConf.AuthCodeURL("state", oauth2.AccessTypeOffline)
}

func (self ThirdUserLogic) LoginFromGitea(ctx context.Context, code string) (*model.User, error) {
objLog := GetLogger(ctx)

giteaUser, token, err := self.giteaTokenAndUser(ctx, code)
if err != nil {
objLog.Errorln("LoginFromGithub githubTokenAndUser error:", err)
return nil, err
}

bindUser := &model.BindUser{}
// 是否已经授权过了
_, err = MasterDB.Where("username=? AND type=?", giteaUser.UserName, model.BindTypeGitea).Get(bindUser)
if err != nil {
objLog.Errorln("LoginFromGithub Get BindUser error:", err)
return nil, err
}

if bindUser.Uid > 0 {
// 更新 token 信息
change := map[string]interface{}{
"access_token": token.AccessToken,
"refresh_token": token.RefreshToken,
}
if !token.Expiry.IsZero() {
change["expire"] = int(token.Expiry.Unix())
}
_, err = MasterDB.Table(new(model.BindUser)).Where("uid=?", bindUser.Uid).Update(change)
if err != nil {
objLog.Errorln("LoginFromGithub update token error:", err)
return nil, err
}

user := DefaultUser.FindOne(ctx, "uid", bindUser.Uid)
return user, nil
}

exists := DefaultUser.EmailOrUsernameExists(ctx, giteaUser.Email, giteaUser.UserName)
if exists {
// TODO: 考虑改进?
objLog.Errorln("LoginFromGithub Github 对应的用户信息被占用")
return nil, errors.New("Github 对应的用户信息被占用,可能你注册过本站,用户名密码登录试试!")
}

session := MasterDB.NewSession()
defer session.Close()
session.Begin()

// 有可能获取不到 email?加上 @gitea.com做邮箱后缀
if giteaUser.Email == "" {
giteaUser.Email = giteaUser.UserName + "@gitea.com"
}
// 生成本站用户
user := &model.User{
Email: giteaUser.Email,
Username: giteaUser.UserName,
Name: model.DisplayName(giteaUser),
City: "",
Company: "",
Gitea: giteaUser.UserName,
Website: "",
Avatar: giteaUser.AvatarURL,
IsThird: 1,
Status: model.UserStatusAudit,
}
err = DefaultUser.doCreateUser(ctx, session, user)
if err != nil {
session.Rollback()
objLog.Errorln("LoginFromGithub doCreateUser error:", err)
return nil, err
}

bindUser = &model.BindUser{
Uid: user.Uid,
Type: model.BindTypeGithub,
Email: user.Email,
Tuid: int(giteaUser.ID),
Username: giteaUser.UserName,
Name: model.DisplayName(giteaUser),
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Avatar: giteaUser.AvatarURL,
}
if !token.Expiry.IsZero() {
bindUser.Expire = int(token.Expiry.Unix())
}
_, err = session.Insert(bindUser)
if err != nil {
session.Rollback()
objLog.Errorln("LoginFromGitea bindUser error:", err)
return nil, err
}

session.Commit()

return user, nil
}

func (self ThirdUserLogic) BindGitea(ctx context.Context, code string, me *model.Me) error {
objLog := GetLogger(ctx)

giteaUser, token, err := self.giteaTokenAndUser(ctx, code)
if err != nil {
objLog.Errorln("LoginFromGitea githubTokenAndUser error:", err)
return err
}

bindUser := &model.BindUser{}
// 是否已经授权过了
_, err = MasterDB.Where("username=? AND type=?", giteaUser.UserName, model.BindTypeGitea).Get(bindUser)
if err != nil {
objLog.Errorln("LoginFromGitea Get BindUser error:", err)
return err
}

if bindUser.Uid > 0 {
// 更新 token 信息
bindUser.AccessToken = token.AccessToken
bindUser.RefreshToken = token.RefreshToken
if !token.Expiry.IsZero() {
bindUser.Expire = int(token.Expiry.Unix())
}
_, err = MasterDB.Where("uid=?", bindUser.Uid).Update(bindUser)
if err != nil {
objLog.Errorln("LoginFromGitea update token error:", err)
return err
}

return nil
}

bindUser = &model.BindUser{
Uid: me.Uid,
Type: model.BindTypeGithub,
Email: giteaUser.Email,
Tuid: int(giteaUser.ID),
Username: giteaUser.UserName,
Name: model.DisplayName(giteaUser),
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Avatar: giteaUser.AvatarURL,
}
if !token.Expiry.IsZero() {
bindUser.Expire = int(token.Expiry.Unix())
}
_, err = MasterDB.Insert(bindUser)
if err != nil {
objLog.Errorln("LoginFromGitea insert bindUser error:", err)
return err
}

return nil
}

func (ThirdUserLogic) UnBindUser(ctx context.Context, bindId interface{}, me *model.Me) error {
if !DefaultUser.HasPasswd(ctx, me.Uid) {
return errors.New("请先设置密码!")
Expand Down Expand Up @@ -243,8 +415,39 @@ func (ThirdUserLogic) githubTokenAndUser(ctx context.Context, code string) (*mod
}

if githubUser.Id == 0 {
return nil, nil, errors.New("get github user info error")
return nil, nil, errors.New("get gitea user info error")
}

return githubUser, token, nil
}

func (ThirdUserLogic) giteaTokenAndUser(ctx context.Context, code string) (*model.GiteaUser, *oauth2.Token, error) {
token, err := giteaConf.Exchange(ctx, code)
if err != nil {
return nil, nil, err
}

httpClient := giteaConf.Client(ctx, token)
resp, err := httpClient.Get(GiteaAPIBaseUrl + "/user")
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()

respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, nil, err
}

giteaUser := &model.GiteaUser{}
err = json.Unmarshal(respBytes, giteaUser)
if err != nil {
return nil, nil, err
}

if giteaUser.ID == 0 {
return nil, nil, errors.New("get gitea user info error")
}

return giteaUser, token, nil
}
13 changes: 13 additions & 0 deletions model/github_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

package model

import (
"code.gitea.io/sdk/gitea"
)

type GithubUser struct {
Id int `json:"id"`
Login string `json:"login"`
Expand All @@ -16,3 +20,12 @@ type GithubUser struct {
Blog string `json:"blog"`
Location string `json:"location"`
}

type GiteaUser = gitea.User

func DisplayName(g *GiteaUser) string {
if g.FullName == "" {
return g.UserName
}
return g.FullName
}
2 changes: 2 additions & 0 deletions model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type User struct {
City string `json:"city"`
Company string `json:"company"`
Github string `json:"github"`
Gitea string `json:"gitea"`
Weibo string `json:"weibo"`
Website string `json:"website"`
Monlog string `json:"monlog"`
Expand Down Expand Up @@ -177,6 +178,7 @@ type UserRole struct {

const (
BindTypeGithub = iota
BindTypeGitea
)

type BindUser struct {
Expand Down
4 changes: 4 additions & 0 deletions template/common/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@
<i class="fa fa-github" aria-hidden="true"></i>
GitHub 登录
</a>
<a id="login-gitea" href="/oauth/gitea/login" class="btn btn-default btn-sm pull-left">
<i class="fa fa-github" aria-hidden="true"></i>
Gitea 登录
</a>
<div class="forget">
<a href="/account/forgetpwd" title="点击找回密码">忘记密码?</a>
</div>
Expand Down
4 changes: 4 additions & 0 deletions template/common/my_info.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ <h3 class="title"><i class="glyphicon glyphicon-user"></i> 用户登录</h3>
<i class="fa fa-github" aria-hidden="true"></i>
GitHub 登录
</a>
<a href="/oauth/gitea/login" class="btn btn-default btn-sm">
<i class="fa fa-github" aria-hidden="true"></i>
Gitea 登录
</a>
</div>
</div>
</form>
Expand Down
Loading