From 9467046f7fec640afe07c9db67910b69cbfafdb1 Mon Sep 17 00:00:00 2001 From: zijiren233 Date: Sat, 21 Oct 2023 18:47:03 +0800 Subject: [PATCH] Fear: impl google oauth2 --- internal/bootstrap/provider.go | 2 +- internal/conf/oauth2.go | 6 +++-- internal/provider/github.go | 24 +++++++++++--------- internal/provider/gitlab.go | 24 +++++++++++--------- internal/provider/google.go | 40 ++++++++++++++++++++++++---------- internal/provider/provider.go | 16 ++++++++++---- server/oauth2/auth.go | 18 +++++---------- utils/syncCache/cache.go | 18 ++++++++++++--- 8 files changed, 95 insertions(+), 53 deletions(-) diff --git a/internal/bootstrap/provider.go b/internal/bootstrap/provider.go index 4152e3af..817ebc36 100644 --- a/internal/bootstrap/provider.go +++ b/internal/bootstrap/provider.go @@ -9,7 +9,7 @@ import ( func InitProvider(ctx context.Context) error { for op, v := range conf.Conf.OAuth2 { - err := provider.InitProvider(op, v.ClientID, v.ClientSecret) + err := provider.InitProvider(op, v.ClientID, v.ClientSecret, provider.WithRedirectURL(v.RedirectURL)) if err != nil { return err } diff --git a/internal/conf/oauth2.go b/internal/conf/oauth2.go index 6672ff28..ef47bc85 100644 --- a/internal/conf/oauth2.go +++ b/internal/conf/oauth2.go @@ -9,13 +9,15 @@ type OAuth2Config map[provider.OAuth2Provider]OAuth2ProviderConfig type OAuth2ProviderConfig struct { ClientID string `yaml:"client_id"` ClientSecret string `yaml:"client_secret"` + RedirectURL string `yaml:"redirect_url"` } func DefaultOAuth2Config() OAuth2Config { return OAuth2Config{ (&provider.GithubProvider{}).Provider(): { - ClientID: "github_client_id", - ClientSecret: "github_client_secret", + ClientID: "", + ClientSecret: "", + RedirectURL: "", }, } } diff --git a/internal/provider/github.go b/internal/provider/github.go index 4040c196..6ff25ca8 100644 --- a/internal/provider/github.go +++ b/internal/provider/github.go @@ -10,25 +10,29 @@ import ( ) type GithubProvider struct { - ClientID, ClientSecret string + config oauth2.Config } -func (p *GithubProvider) Init(ClientID, ClientSecret string) { - p.ClientID = ClientID - p.ClientSecret = ClientSecret +func (p *GithubProvider) Init(ClientID, ClientSecret string, options ...Oauth2Option) { + p.config.ClientID = ClientID + p.config.ClientSecret = ClientSecret + p.config.Scopes = []string{"user"} + p.config.Endpoint = github.Endpoint + for _, o := range options { + o(&p.config) + } } func (p *GithubProvider) Provider() OAuth2Provider { return "github" } -func (p *GithubProvider) NewConfig() *oauth2.Config { - return &oauth2.Config{ - ClientID: p.ClientID, - ClientSecret: p.ClientSecret, - Scopes: []string{"user"}, - Endpoint: github.Endpoint, +func (p *GithubProvider) NewConfig(options ...Oauth2Option) *oauth2.Config { + c := p.config + for _, o := range options { + o(&c) } + return &c } func (p *GithubProvider) GetUserInfo(ctx context.Context, config *oauth2.Config, code string) (*UserInfo, error) { diff --git a/internal/provider/gitlab.go b/internal/provider/gitlab.go index 28159512..82708b4c 100644 --- a/internal/provider/gitlab.go +++ b/internal/provider/gitlab.go @@ -9,25 +9,29 @@ import ( ) type GitlabProvider struct { - ClientID, ClientSecret string + config oauth2.Config } -func (g *GitlabProvider) Init(ClientID, ClientSecret string) { - g.ClientID = ClientID - g.ClientSecret = ClientSecret +func (g *GitlabProvider) Init(ClientID, ClientSecret string, options ...Oauth2Option) { + g.config.ClientID = ClientID + g.config.ClientSecret = ClientSecret + g.config.Scopes = []string{"read_user"} + g.config.Endpoint = gitlab.Endpoint + for _, o := range options { + o(&g.config) + } } func (g *GitlabProvider) Provider() OAuth2Provider { return "gitlab" } -func (g *GitlabProvider) NewConfig() *oauth2.Config { - return &oauth2.Config{ - ClientID: g.ClientID, - ClientSecret: g.ClientSecret, - Scopes: []string{"read_user"}, - Endpoint: gitlab.Endpoint, +func (g *GitlabProvider) NewConfig(options ...Oauth2Option) *oauth2.Config { + c := g.config + for _, o := range options { + o(&c) } + return &c } func (g *GitlabProvider) GetUserInfo(ctx context.Context, config *oauth2.Config, code string) (*UserInfo, error) { diff --git a/internal/provider/google.go b/internal/provider/google.go index 993510c4..47aa4b29 100644 --- a/internal/provider/google.go +++ b/internal/provider/google.go @@ -4,30 +4,35 @@ import ( "context" "net/http" + json "github.com/json-iterator/go" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) type GoogleProvider struct { - ClientID, ClientSecret string + config oauth2.Config } -func (g *GoogleProvider) Init(ClientID, ClientSecret string) { - g.ClientID = ClientID - g.ClientSecret = ClientSecret +func (g *GoogleProvider) Init(ClientID, ClientSecret string, options ...Oauth2Option) { + g.config.ClientID = ClientID + g.config.ClientSecret = ClientSecret + g.config.Scopes = []string{"profile"} + g.config.Endpoint = google.Endpoint + for _, o := range options { + o(&g.config) + } } func (g *GoogleProvider) Provider() OAuth2Provider { return "google" } -func (g *GoogleProvider) NewConfig() *oauth2.Config { - return &oauth2.Config{ - ClientID: g.ClientID, - ClientSecret: g.ClientSecret, - Scopes: []string{"profile"}, - Endpoint: google.Endpoint, +func (g *GoogleProvider) NewConfig(options ...Oauth2Option) *oauth2.Config { + c := g.config + for _, o := range options { + o(&c) } + return &c } func (g *GoogleProvider) GetUserInfo(ctx context.Context, config *oauth2.Config, code string) (*UserInfo, error) { @@ -45,9 +50,22 @@ func (g *GoogleProvider) GetUserInfo(ctx context.Context, config *oauth2.Config, return nil, err } defer resp.Body.Close() - return nil, FormatErrNotImplemented("google") + ui := googleUserInfo{} + err = json.NewDecoder(resp.Body).Decode(&ui) + if err != nil { + return nil, err + } + return &UserInfo{ + Username: ui.Name, + ProviderUserID: ui.ID, + }, nil } func init() { registerProvider(new(GoogleProvider)) } + +type googleUserInfo struct { + ID uint `json:"id,string"` + Name string `json:"name"` +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index adfb923e..8f99d041 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -19,19 +19,27 @@ type UserInfo struct { ProviderUserID uint } +type Oauth2Option func(*oauth2.Config) + +func WithRedirectURL(url string) Oauth2Option { + return func(c *oauth2.Config) { + c.RedirectURL = url + } +} + type ProviderInterface interface { - Init(ClientID, ClientSecret string) + Init(ClientID, ClientSecret string, options ...Oauth2Option) Provider() OAuth2Provider - NewConfig() *oauth2.Config + NewConfig(options ...Oauth2Option) *oauth2.Config GetUserInfo(ctx context.Context, config *oauth2.Config, code string) (*UserInfo, error) } -func InitProvider(p OAuth2Provider, ClientID, ClientSecret string) error { +func InitProvider(p OAuth2Provider, ClientID, ClientSecret string, options ...Oauth2Option) error { pi, ok := allowedProviders[p] if !ok { return FormatErrNotImplemented(p) } - pi.Init(ClientID, ClientSecret) + pi.Init(ClientID, ClientSecret, options...) if enabledProviders == nil { enabledProviders = make(map[OAuth2Provider]ProviderInterface) } diff --git a/server/oauth2/auth.go b/server/oauth2/auth.go index 075589c5..d24cee45 100644 --- a/server/oauth2/auth.go +++ b/server/oauth2/auth.go @@ -5,7 +5,6 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/server/middlewares" @@ -16,9 +15,9 @@ import ( // /oauth2/login/:type func OAuth2(ctx *gin.Context) { - p := provider.OAuth2Provider(ctx.Param("type")) + t := ctx.Param("type") - pi, err := provider.GetProvider(p) + pi, err := provider.GetProvider(provider.OAuth2Provider(t)) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) return @@ -31,9 +30,8 @@ func OAuth2(ctx *gin.Context) { } func OAuth2Api(ctx *gin.Context) { - p := provider.OAuth2Provider(ctx.Param("type")) - - pi, err := provider.GetProvider(p) + t := ctx.Param("type") + pi, err := provider.GetProvider(provider.OAuth2Provider(t)) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) } @@ -48,8 +46,6 @@ func OAuth2Api(ctx *gin.Context) { // /oauth2/callback/:type func OAuth2Callback(ctx *gin.Context) { - p := provider.OAuth2Provider(ctx.Param("type")) - code := ctx.Query("code") if code == "" { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorStringResp("invalid oauth2 code")) @@ -68,6 +64,7 @@ func OAuth2Callback(ctx *gin.Context) { return } + p := provider.OAuth2Provider(ctx.Param("type")) pi, err := provider.GetProvider(p) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) @@ -91,15 +88,11 @@ func OAuth2Callback(ctx *gin.Context) { return } - logrus.Info("asdasd") - RenderToken(ctx, "/web/", token) } // /oauth2/callback/:type func OAuth2CallbackApi(ctx *gin.Context) { - p := provider.OAuth2Provider(ctx.Param("type")) - req := model.OAuth2CallbackReq{} if err := req.Decode(ctx); err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) @@ -112,6 +105,7 @@ func OAuth2CallbackApi(ctx *gin.Context) { return } + p := provider.OAuth2Provider(ctx.Param("type")) pi, err := provider.GetProvider(p) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) diff --git a/utils/syncCache/cache.go b/utils/syncCache/cache.go index cfd769b1..bdd544b2 100644 --- a/utils/syncCache/cache.go +++ b/utils/syncCache/cache.go @@ -7,8 +7,17 @@ import ( ) type SyncCache[K comparable, V any] struct { - cache rwmap.RWMap[K, *entry[V]] - ticker *time.Ticker + cache rwmap.RWMap[K, *entry[V]] + deletedCallback func(v V) + ticker *time.Ticker +} + +type SyncCacheConfig[K comparable, V any] func(sc *SyncCache[K, V]) + +func WithDeletedCallback[K comparable, V any](callback func(v V)) SyncCacheConfig[K, V] { + return func(sc *SyncCache[K, V]) { + sc.deletedCallback = callback + } } func NewSyncCache[K comparable, V any](trimTime time.Duration) *SyncCache[K, V] { @@ -31,7 +40,10 @@ func (sc *SyncCache[K, V]) Releases() { func (sc *SyncCache[K, V]) trim() { sc.cache.Range(func(key K, value *entry[V]) bool { if value.IsExpired() { - sc.cache.Delete(key) + e, loaded := sc.cache.LoadAndDelete(key) + if loaded && sc.deletedCallback != nil { + sc.deletedCallback(e.value) + } } return true })