dragonfly/manager/auth/oauth/oauth.go

146 lines
3.4 KiB
Go

package oauth
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
"d7y.io/dragonfly/v2/manager/model"
"d7y.io/dragonfly/v2/pkg/util/stringutils"
"golang.org/x/crypto/bcrypt"
"golang.org/x/oauth2"
"gorm.io/gorm"
)
const (
GoogleScopes = "https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile"
GoogleUserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo"
GithubScopes = "user,public_repo"
GithubUserInfoURL = "https://api.github.com/user"
)
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) (*oauth2User, error)
ExchangeUserByCode(string, *gorm.DB) (*model.User, error)
AuthCodeURL() string
}
func NewBaseOauth2(name string, clientID string, clientSecret string, scopes string, authURL string, tokenURL string, db *gorm.DB) (Oauther, error) {
oa := &baseOauth2{
Name: name,
Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: strings.Split(scopes, ","),
Endpoint: oauth2.Endpoint{
AuthURL: authURL,
TokenURL: tokenURL,
},
},
}
redirectURL, err := oa.GetRediectURL(db)
if err != nil {
return nil, err
}
oa.Config.RedirectURL = redirectURL
return oa, nil
}
func (oa *baseOauth2) GetRediectURL(db *gorm.DB) (string, error) {
s := model.Settings{}
if err := db.First(&s, model.Settings{
Key: "server_domain",
}).Error; err != nil {
return "", err
}
return fmt.Sprintf("%s/api/v1/oauth/%s/sigin", s.Value, oa.Name), nil
}
func (oa *baseOauth2) AuthCodeURL() string {
return oa.Config.AuthCodeURL(stringutils.RandString(5))
}
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
}
user := model.User{}
if err := db.Model(model.User{}).Where("name = ?", u.Name).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}