From cb126da5d9965a34eaa6a85d2405b157ff46afb3 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Wed, 11 Aug 2021 13:50:43 +0800 Subject: [PATCH] update oauth to use oauth2 library Signed-off-by: yxxhero --- manager/oauth/github.go | 58 ++++++++++++++++++++++++ manager/oauth/google.go | 27 +++++++----- manager/oauth/oauth.go | 95 ++++++++++++++++++++++++++++++++++++---- manager/service/oauth.go | 8 ++-- 4 files changed, 164 insertions(+), 24 deletions(-) diff --git a/manager/oauth/github.go b/manager/oauth/github.go index c90af7b36..8ad39d9d0 100644 --- a/manager/oauth/github.go +++ b/manager/oauth/github.go @@ -1 +1,59 @@ package oauth + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/github" + "gorm.io/gorm" +) + +type githubOauth2 struct { + baseOauth2 +} + +func NewGithubOauth2(name string, clientID string, clientSecret string, db *gorm.DB) (Oauther, error) { + + oa := &githubOauth2{} + oa.Name = name + oa.Config = &oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/userinfo.email"}, + Endpoint: github.Endpoint, + } + oa.UserInfoURL = "https://api.github.com/user" + + redirectURL, err := oa.GetRediectURL(db) + if err != nil { + return nil, err + } + oa.Config.RedirectURL = redirectURL + return oa, nil +} + +func (oa *githubOauth2) GetOauthUserInfo(token string) (*oauth2User, error) { + req, err := http.NewRequest("GET", oa.UserInfoURL, nil) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "token"+" "+token) + response, err := (&http.Client{}).Do(req) + if err != nil { + return nil, err + } + defer response.Body.Close() + contents, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + u := oauth2User{} + err = json.Unmarshal(contents, &u) + if err != nil { + return nil, err + } + return &u, nil +} diff --git a/manager/oauth/google.go b/manager/oauth/google.go index 93402f93b..036366ad4 100644 --- a/manager/oauth/google.go +++ b/manager/oauth/google.go @@ -1,27 +1,32 @@ package oauth + import ( + "golang.org/x/oauth2" "golang.org/x/oauth2/google" + "gorm.io/gorm" ) type googleOauth2 struct { - oauth2 + baseOauth2 } -func NewGoogle(name string, clientID string, clientSecret string, scopes string, authURL string, tokenURL string, db *gorm.DB) (*oauth, error) { +func NewGoogleOauth2(name string, clientID string, clientSecret string, db *gorm.DB) (Oauther, error) { - oa := &googleOauth2{ - Name: name, - Config: &oauth2.Config{ - ClientID: clientID, - ClientSecret: clientSecret, - Scopes: strings.Split(scopes, ","), - Endpoint: google. - }, + oa := &googleOauth2{} + oa.Name = name + oa.Config = &oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/userinfo.email"}, + Endpoint: google.Endpoint, } + oa.UserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo" + redirectURL, err := oa.GetRediectURL(db) if err != nil { return nil, err } oa.Config.RedirectURL = redirectURL return oa, nil -} \ No newline at end of file +} diff --git a/manager/oauth/oauth.go b/manager/oauth/oauth.go index a4781b8a1..fb1c2159a 100644 --- a/manager/oauth/oauth.go +++ b/manager/oauth/oauth.go @@ -1,29 +1,42 @@ package oauth import ( + "context" + "encoding/json" + "errors" "fmt" + "io/ioutil" + "net/http" "strings" "d7y.io/dragonfly/v2/manager/model" + "golang.org/x/crypto/bcrypt" "golang.org/x/oauth2" "gorm.io/gorm" ) -type oauth2 struct { +type baseOauth2 struct { Name string UserInfoURL string Config *oauth2.Config } +type oauth2User struct { + Name string `json:"name"` + Email string `json:"email"` +} + // oauth interface type Oauther interface { GetRediectURL(*gorm.DB) (string, error) - GetOauthUserInfo(string) (*model.User, error) + GetOauthUserInfo(string) (*oauth2User, error) + ExchangeUserByCode(string, *gorm.DB) (*model.User, error) + AuthCodeURL(string) string } -func NewOauth(name string, clientID string, clientSecret string, scopes string, authURL string, tokenURL string, db *gorm.DB) (*oauth, error) { +func NewBaseOauth2(name string, clientID string, clientSecret string, scopes string, authURL string, tokenURL string, db *gorm.DB) (Oauther, error) { - oa := &oauth2{ + oa := &baseOauth2{ Name: name, Config: &oauth2.Config{ ClientID: clientID, @@ -43,7 +56,7 @@ func NewOauth(name string, clientID string, clientSecret string, scopes string, return oa, nil } -func (o *oauth2) GetRediectURL(db *gorm.DB) (string, error) { +func (oa *baseOauth2) GetRediectURL(db *gorm.DB) (string, error) { s := model.Settings{} if err := db.First(&s, model.Settings{ @@ -51,10 +64,74 @@ func (o *oauth2) GetRediectURL(db *gorm.DB) (string, error) { }).Error; err != nil { return "", err } - return fmt.Sprintf("%s/api/v1/oauth/%s/sigin", s.Value, o.Name), nil + return fmt.Sprintf("%s/api/v1/oauth/%s/sigin", s.Value, oa.Name), nil } -func (o *oauth2) GetOauthUserInfo(code string) (*model.User, error) { - user := model.User{} - return &user, nil +func (oa *baseOauth2) AuthCodeURL(state string) string { + return oa.Config.AuthCodeURL(state) +} + +func (oa *baseOauth2) GetOauthUserInfo(token string) (*oauth2User, error) { + response, err := http.Get(fmt.Sprintf("%s?access_token=%s", oa.UserInfoURL, token)) + if err != nil { + return nil, err + } + defer response.Body.Close() + contents, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + u := oauth2User{} + err = json.Unmarshal(contents, &u) + if err != nil { + return nil, err + } + return &u, nil +} + +func (oa *baseOauth2) ExchangeUserByCode(code string, db *gorm.DB) (*model.User, error) { + token, err := oa.Config.Exchange(context.Background(), code) + if err != nil { + return nil, err + } + if oa.UserInfoURL == "" { + return nil, errors.New("UserInfoURL is empty") + } + u, err := oa.GetOauthUserInfo(token.AccessToken) + + if err != nil { + return nil, err + } + + if u.Name == "admin" { + return nil, errors.New("admin is not allowed to login by oauth") + } + encryptedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte("Dragonfly2"), bcrypt.MinCost) + if err != nil { + return nil, err + } + var userCount int64 + if err := db.Model(model.User{}).Where("name = ?", u.Name).Count(&userCount).Error; err != nil { + return nil, err + } + if userCount <= 0 { + user := model.User{ + EncryptedPassword: string(encryptedPasswordBytes), + Name: u.Name, + Email: u.Email, + } + + if err := db.Create(&user).Error; err != nil { + return nil, err + } + return &user, nil + + } else { + user := model.User{} + if err := db.Model(model.User{}).Where("name = ?", u.Name).First(&user).Error; err != nil { + return nil, err + } + return &user, nil + } + } diff --git a/manager/service/oauth.go b/manager/service/oauth.go index ea06ba1eb..5ca4ba246 100644 --- a/manager/service/oauth.go +++ b/manager/service/oauth.go @@ -95,11 +95,11 @@ func (s *rest) OauthSignin(name string) (string, error) { return "", err } - o, err := oauth.NewOauth(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) + o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) if err != nil { return "", err } - return o.Config.AuthCodeURL("state"), nil + return o.AuthCodeURL("state"), nil } func (s *rest) OauthCallback(name, code string) (*model.User, error) { @@ -108,11 +108,11 @@ func (s *rest) OauthCallback(name, code string) (*model.User, error) { return nil, err } - o, err := oauth.NewOauth(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) + o, err := oauth.NewBaseOauth2(name, oauthModel.ClientID, oauthModel.ClientSecret, oauthModel.Scopes, oauthModel.AuthURL, oauthModel.TokenURL, s.db) if err != nil { return nil, err } - user, err := o.GetOauthUserInfo(code) + user, err := o.ExchangeUserByCode(code, s.db) if err != nil { return nil, err }