parent
							
								
									8c266f2df5
								
							
						
					
					
						commit
						4b9b8024ba
					
				@ -1,228 +0,0 @@ | 
				
			||||
// Copyright 2014 Google Inc. All Rights Reserved.
 | 
				
			||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
// Package oauth2 contains Martini handlers to provide
 | 
				
			||||
// user login via an OAuth 2.0 backend.
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"encoding/json" | 
				
			||||
	"net/http" | 
				
			||||
	"net/url" | 
				
			||||
	"strings" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"code.google.com/p/goauth2/oauth" | 
				
			||||
	"github.com/go-martini/martini" | 
				
			||||
 | 
				
			||||
	"github.com/gogits/session" | 
				
			||||
 | 
				
			||||
	"github.com/gogits/gogs/modules/log" | 
				
			||||
	"github.com/gogits/gogs/modules/middleware" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
const ( | 
				
			||||
	keyToken    = "oauth2_token" | 
				
			||||
	keyNextPage = "next" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	// Path to handle OAuth 2.0 logins.
 | 
				
			||||
	PathLogin = "/login" | 
				
			||||
	// Path to handle OAuth 2.0 logouts.
 | 
				
			||||
	PathLogout = "/logout" | 
				
			||||
	// Path to handle callback from OAuth 2.0 backend
 | 
				
			||||
	// to exchange credentials.
 | 
				
			||||
	PathCallback = "/oauth2callback" | 
				
			||||
	// Path to handle error cases.
 | 
				
			||||
	PathError = "/oauth2error" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Represents OAuth2 backend options.
 | 
				
			||||
type Options struct { | 
				
			||||
	ClientId     string | 
				
			||||
	ClientSecret string | 
				
			||||
	RedirectURL  string | 
				
			||||
	Scopes       []string | 
				
			||||
 | 
				
			||||
	AuthUrl  string | 
				
			||||
	TokenUrl string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Represents a container that contains
 | 
				
			||||
// user's OAuth 2.0 access and refresh tokens.
 | 
				
			||||
type Tokens interface { | 
				
			||||
	Access() string | 
				
			||||
	Refresh() string | 
				
			||||
	IsExpired() bool | 
				
			||||
	ExpiryTime() time.Time | 
				
			||||
	ExtraData() map[string]string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type token struct { | 
				
			||||
	oauth.Token | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (t *token) ExtraData() map[string]string { | 
				
			||||
	return t.Extra | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns the access token.
 | 
				
			||||
func (t *token) Access() string { | 
				
			||||
	return t.AccessToken | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns the refresh token.
 | 
				
			||||
func (t *token) Refresh() string { | 
				
			||||
	return t.RefreshToken | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns whether the access token is
 | 
				
			||||
// expired or not.
 | 
				
			||||
func (t *token) IsExpired() bool { | 
				
			||||
	if t == nil { | 
				
			||||
		return true | 
				
			||||
	} | 
				
			||||
	return t.Expired() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns the expiry time of the user's
 | 
				
			||||
// access token.
 | 
				
			||||
func (t *token) ExpiryTime() time.Time { | 
				
			||||
	return t.Expiry | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns a new Google OAuth 2.0 backend endpoint.
 | 
				
			||||
func Google(opts *Options) martini.Handler { | 
				
			||||
	opts.AuthUrl = "https://accounts.google.com/o/oauth2/auth" | 
				
			||||
	opts.TokenUrl = "https://accounts.google.com/o/oauth2/token" | 
				
			||||
	return NewOAuth2Provider(opts) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns a new Github OAuth 2.0 backend endpoint.
 | 
				
			||||
func Github(opts *Options) martini.Handler { | 
				
			||||
	opts.AuthUrl = "https://github.com/login/oauth/authorize" | 
				
			||||
	opts.TokenUrl = "https://github.com/login/oauth/access_token" | 
				
			||||
	return NewOAuth2Provider(opts) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Facebook(opts *Options) martini.Handler { | 
				
			||||
	opts.AuthUrl = "https://www.facebook.com/dialog/oauth" | 
				
			||||
	opts.TokenUrl = "https://graph.facebook.com/oauth/access_token" | 
				
			||||
	return NewOAuth2Provider(opts) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns a generic OAuth 2.0 backend endpoint.
 | 
				
			||||
func NewOAuth2Provider(opts *Options) martini.Handler { | 
				
			||||
	config := &oauth.Config{ | 
				
			||||
		ClientId:     opts.ClientId, | 
				
			||||
		ClientSecret: opts.ClientSecret, | 
				
			||||
		RedirectURL:  opts.RedirectURL, | 
				
			||||
		Scope:        strings.Join(opts.Scopes, " "), | 
				
			||||
		AuthURL:      opts.AuthUrl, | 
				
			||||
		TokenURL:     opts.TokenUrl, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	transport := &oauth.Transport{ | 
				
			||||
		Config:    config, | 
				
			||||
		Transport: http.DefaultTransport, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return func(c martini.Context, ctx *middleware.Context) { | 
				
			||||
		if ctx.Req.Method == "GET" { | 
				
			||||
			switch ctx.Req.URL.Path { | 
				
			||||
			case PathLogin: | 
				
			||||
				login(transport, ctx) | 
				
			||||
			case PathLogout: | 
				
			||||
				logout(transport, ctx) | 
				
			||||
			case PathCallback: | 
				
			||||
				handleOAuth2Callback(transport, ctx) | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		tk := unmarshallToken(ctx.Session) | 
				
			||||
		if tk != nil { | 
				
			||||
			// check if the access token is expired
 | 
				
			||||
			if tk.IsExpired() && tk.Refresh() == "" { | 
				
			||||
				ctx.Session.Delete(keyToken) | 
				
			||||
				tk = nil | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		// Inject tokens.
 | 
				
			||||
		c.MapTo(tk, (*Tokens)(nil)) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Handler that redirects user to the login page
 | 
				
			||||
// if user is not logged in.
 | 
				
			||||
// Sample usage:
 | 
				
			||||
// m.Get("/login-required", oauth2.LoginRequired, func() ... {})
 | 
				
			||||
var LoginRequired martini.Handler = func() martini.Handler { | 
				
			||||
	return func(c martini.Context, ctx *middleware.Context) { | 
				
			||||
		token := unmarshallToken(ctx.Session) | 
				
			||||
		if token == nil || token.IsExpired() { | 
				
			||||
			next := url.QueryEscape(ctx.Req.URL.RequestURI()) | 
				
			||||
			ctx.Redirect(PathLogin + "?next=" + next) | 
				
			||||
			return | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
}() | 
				
			||||
 | 
				
			||||
func login(t *oauth.Transport, ctx *middleware.Context) { | 
				
			||||
	next := extractPath(ctx.Query(keyNextPage)) | 
				
			||||
	if ctx.Session.Get(keyToken) == nil { | 
				
			||||
		// User is not logged in.
 | 
				
			||||
		ctx.Redirect(t.Config.AuthCodeURL(next)) | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	// No need to login, redirect to the next page.
 | 
				
			||||
	ctx.Redirect(next) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func logout(t *oauth.Transport, ctx *middleware.Context) { | 
				
			||||
	next := extractPath(ctx.Query(keyNextPage)) | 
				
			||||
	ctx.Session.Delete(keyToken) | 
				
			||||
	ctx.Redirect(next) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func handleOAuth2Callback(t *oauth.Transport, ctx *middleware.Context) { | 
				
			||||
	if errMsg := ctx.Query("error_description"); len(errMsg) > 0 { | 
				
			||||
		log.Error("oauth2.handleOAuth2Callback: %s", errMsg) | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	next := extractPath(ctx.Query("state")) | 
				
			||||
	code := ctx.Query("code") | 
				
			||||
	tk, err := t.Exchange(code) | 
				
			||||
	if err != nil { | 
				
			||||
		// Pass the error message, or allow dev to provide its own
 | 
				
			||||
		// error handler.
 | 
				
			||||
		log.Error("oauth2.handleOAuth2Callback(token.Exchange): %v", err) | 
				
			||||
		// ctx.Redirect(PathError)
 | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	// Store the credentials in the session.
 | 
				
			||||
	val, _ := json.Marshal(tk) | 
				
			||||
	ctx.Session.Set(keyToken, val) | 
				
			||||
	ctx.Redirect(next) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func unmarshallToken(s session.SessionStore) (t *token) { | 
				
			||||
	if s.Get(keyToken) == nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	data := s.Get(keyToken).([]byte) | 
				
			||||
	var tk oauth.Token | 
				
			||||
	json.Unmarshal(data, &tk) | 
				
			||||
	return &token{tk} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func extractPath(next string) string { | 
				
			||||
	n, err := url.Parse(next) | 
				
			||||
	if err != nil { | 
				
			||||
		return "/" | 
				
			||||
	} | 
				
			||||
	return n.Path | 
				
			||||
} | 
				
			||||
@ -1,162 +0,0 @@ | 
				
			||||
// Copyright 2014 Google Inc. All Rights Reserved.
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package oauth2 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"net/http" | 
				
			||||
	"net/http/httptest" | 
				
			||||
	"testing" | 
				
			||||
 | 
				
			||||
	"github.com/go-martini/martini" | 
				
			||||
	"github.com/martini-contrib/sessions" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func Test_LoginRedirect(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	m := martini.New() | 
				
			||||
	m.Use(sessions.Sessions("my_session", sessions.NewCookieStore([]byte("secret123")))) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
		ClientId:     "client_id", | 
				
			||||
		ClientSecret: "client_secret", | 
				
			||||
		RedirectURL:  "refresh_url", | 
				
			||||
		Scopes:       []string{"x", "y"}, | 
				
			||||
	})) | 
				
			||||
 | 
				
			||||
	r, _ := http.NewRequest("GET", "/login", nil) | 
				
			||||
	m.ServeHTTP(recorder, r) | 
				
			||||
 | 
				
			||||
	location := recorder.HeaderMap["Location"][0] | 
				
			||||
	if recorder.Code != 302 { | 
				
			||||
		t.Errorf("Not being redirected to the auth page.") | 
				
			||||
	} | 
				
			||||
	if location != "https://accounts.google.com/o/oauth2/auth?access_type=&approval_prompt=&client_id=client_id&redirect_uri=refresh_url&response_type=code&scope=x+y&state=" { | 
				
			||||
		t.Errorf("Not being redirected to the right page, %v found", location) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Test_LoginRedirectAfterLoginRequired(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	m := martini.Classic() | 
				
			||||
	m.Use(sessions.Sessions("my_session", sessions.NewCookieStore([]byte("secret123")))) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
		ClientId:     "client_id", | 
				
			||||
		ClientSecret: "client_secret", | 
				
			||||
		RedirectURL:  "refresh_url", | 
				
			||||
		Scopes:       []string{"x", "y"}, | 
				
			||||
	})) | 
				
			||||
 | 
				
			||||
	m.Get("/login-required", LoginRequired, func(tokens Tokens) (int, string) { | 
				
			||||
		return 200, tokens.Access() | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	r, _ := http.NewRequest("GET", "/login-required?key=value", nil) | 
				
			||||
	m.ServeHTTP(recorder, r) | 
				
			||||
 | 
				
			||||
	location := recorder.HeaderMap["Location"][0] | 
				
			||||
	if recorder.Code != 302 { | 
				
			||||
		t.Errorf("Not being redirected to the auth page.") | 
				
			||||
	} | 
				
			||||
	if location != "/login?next=%2Flogin-required%3Fkey%3Dvalue" { | 
				
			||||
		t.Errorf("Not being redirected to the right page, %v found", location) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Test_Logout(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	s := sessions.NewCookieStore([]byte("secret123")) | 
				
			||||
 | 
				
			||||
	m := martini.Classic() | 
				
			||||
	m.Use(sessions.Sessions("my_session", s)) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
	// no need to configure
 | 
				
			||||
	})) | 
				
			||||
 | 
				
			||||
	m.Get("/", func(s sessions.Session) { | 
				
			||||
		s.Set(keyToken, "dummy token") | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	m.Get("/get", func(s sessions.Session) { | 
				
			||||
		if s.Get(keyToken) != nil { | 
				
			||||
			t.Errorf("User credentials are still kept in the session.") | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	logout, _ := http.NewRequest("GET", "/logout", nil) | 
				
			||||
	index, _ := http.NewRequest("GET", "/", nil) | 
				
			||||
 | 
				
			||||
	m.ServeHTTP(httptest.NewRecorder(), index) | 
				
			||||
	m.ServeHTTP(recorder, logout) | 
				
			||||
 | 
				
			||||
	if recorder.Code != 302 { | 
				
			||||
		t.Errorf("Not being redirected to the next page.") | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Test_LogoutOnAccessTokenExpiration(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	s := sessions.NewCookieStore([]byte("secret123")) | 
				
			||||
 | 
				
			||||
	m := martini.Classic() | 
				
			||||
	m.Use(sessions.Sessions("my_session", s)) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
	// no need to configure
 | 
				
			||||
	})) | 
				
			||||
 | 
				
			||||
	m.Get("/addtoken", func(s sessions.Session) { | 
				
			||||
		s.Set(keyToken, "dummy token") | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	m.Get("/", func(s sessions.Session) { | 
				
			||||
		if s.Get(keyToken) != nil { | 
				
			||||
			t.Errorf("User not logged out although access token is expired.") | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	addtoken, _ := http.NewRequest("GET", "/addtoken", nil) | 
				
			||||
	index, _ := http.NewRequest("GET", "/", nil) | 
				
			||||
	m.ServeHTTP(recorder, addtoken) | 
				
			||||
	m.ServeHTTP(recorder, index) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Test_InjectedTokens(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	m := martini.Classic() | 
				
			||||
	m.Use(sessions.Sessions("my_session", sessions.NewCookieStore([]byte("secret123")))) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
	// no need to configure
 | 
				
			||||
	})) | 
				
			||||
	m.Get("/", func(tokens Tokens) string { | 
				
			||||
		return "Hello world!" | 
				
			||||
	}) | 
				
			||||
	r, _ := http.NewRequest("GET", "/", nil) | 
				
			||||
	m.ServeHTTP(recorder, r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Test_LoginRequired(t *testing.T) { | 
				
			||||
	recorder := httptest.NewRecorder() | 
				
			||||
	m := martini.Classic() | 
				
			||||
	m.Use(sessions.Sessions("my_session", sessions.NewCookieStore([]byte("secret123")))) | 
				
			||||
	m.Use(Google(&Options{ | 
				
			||||
	// no need to configure
 | 
				
			||||
	})) | 
				
			||||
	m.Get("/", LoginRequired, func(tokens Tokens) string { | 
				
			||||
		return "Hello world!" | 
				
			||||
	}) | 
				
			||||
	r, _ := http.NewRequest("GET", "/", nil) | 
				
			||||
	m.ServeHTTP(recorder, r) | 
				
			||||
	if recorder.Code != 302 { | 
				
			||||
		t.Errorf("Not being redirected to the auth page although user is not logged in.") | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,333 @@ | 
				
			||||
// Copyright 2014 Google Inc. All Rights Reserved.
 | 
				
			||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package social | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"encoding/json" | 
				
			||||
	"net/http" | 
				
			||||
	"net/url" | 
				
			||||
	"strconv" | 
				
			||||
	"strings" | 
				
			||||
 | 
				
			||||
	"code.google.com/p/goauth2/oauth" | 
				
			||||
 | 
				
			||||
	"github.com/gogits/gogs/models" | 
				
			||||
	"github.com/gogits/gogs/modules/base" | 
				
			||||
	"github.com/gogits/gogs/modules/log" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type BasicUserInfo struct { | 
				
			||||
	Identity string | 
				
			||||
	Name     string | 
				
			||||
	Email    string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type SocialConnector interface { | 
				
			||||
	Type() int | 
				
			||||
	SetRedirectUrl(string) | 
				
			||||
	UserInfo(*oauth.Token, *url.URL) (*BasicUserInfo, error) | 
				
			||||
 | 
				
			||||
	AuthCodeURL(string) string | 
				
			||||
	Exchange(string) (*oauth.Token, error) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	SocialBaseUrl = "/user/login" | 
				
			||||
	SocialMap     = make(map[string]SocialConnector) | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func NewOauthService() { | 
				
			||||
	if !base.Cfg.MustBool("oauth", "ENABLED") { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	base.OauthService = &base.Oauther{} | 
				
			||||
	base.OauthService.OauthInfos = make(map[string]*base.OauthInfo) | 
				
			||||
 | 
				
			||||
	socialConfigs := make(map[string]*oauth.Config) | 
				
			||||
	allOauthes := []string{"github", "google", "qq", "twitter"} | 
				
			||||
	// Load all OAuth config data.
 | 
				
			||||
	for _, name := range allOauthes { | 
				
			||||
		base.OauthService.OauthInfos[name] = &base.OauthInfo{ | 
				
			||||
			ClientId:     base.Cfg.MustValue("oauth."+name, "CLIENT_ID"), | 
				
			||||
			ClientSecret: base.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"), | 
				
			||||
			Scopes:       base.Cfg.MustValue("oauth."+name, "SCOPES"), | 
				
			||||
			AuthUrl:      base.Cfg.MustValue("oauth."+name, "AUTH_URL"), | 
				
			||||
			TokenUrl:     base.Cfg.MustValue("oauth."+name, "TOKEN_URL"), | 
				
			||||
		} | 
				
			||||
		socialConfigs[name] = &oauth.Config{ | 
				
			||||
			ClientId:     base.OauthService.OauthInfos[name].ClientId, | 
				
			||||
			ClientSecret: base.OauthService.OauthInfos[name].ClientSecret, | 
				
			||||
			RedirectURL:  strings.TrimSuffix(base.AppUrl, "/") + SocialBaseUrl + name, | 
				
			||||
			Scope:        base.OauthService.OauthInfos[name].Scopes, | 
				
			||||
			AuthURL:      base.OauthService.OauthInfos[name].AuthUrl, | 
				
			||||
			TokenURL:     base.OauthService.OauthInfos[name].TokenUrl, | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	enabledOauths := make([]string, 0, 10) | 
				
			||||
 | 
				
			||||
	// GitHub.
 | 
				
			||||
	if base.Cfg.MustBool("oauth.github", "ENABLED") { | 
				
			||||
		base.OauthService.GitHub = true | 
				
			||||
		newGitHubOauth(socialConfigs["github"]) | 
				
			||||
		enabledOauths = append(enabledOauths, "GitHub") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// Google.
 | 
				
			||||
	if base.Cfg.MustBool("oauth.google", "ENABLED") { | 
				
			||||
		base.OauthService.Google = true | 
				
			||||
		newGoogleOauth(socialConfigs["google"]) | 
				
			||||
		enabledOauths = append(enabledOauths, "Google") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// QQ.
 | 
				
			||||
	if base.Cfg.MustBool("oauth.qq", "ENABLED") { | 
				
			||||
		base.OauthService.Tencent = true | 
				
			||||
		newTencentOauth(socialConfigs["qq"]) | 
				
			||||
		enabledOauths = append(enabledOauths, "QQ") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// Twitter.
 | 
				
			||||
	if base.Cfg.MustBool("oauth.twitter", "ENABLED") { | 
				
			||||
		base.OauthService.Twitter = true | 
				
			||||
		newTwitterOauth(socialConfigs["twitter"]) | 
				
			||||
		enabledOauths = append(enabledOauths, "Twitter") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	log.Info("Oauth Service Enabled %s", enabledOauths) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//   ________.__  __     ___ ___      ___.
 | 
				
			||||
//  /  _____/|__|/  |_  /   |   \ __ _\_ |__
 | 
				
			||||
// /   \  ___|  \   __\/    ~    \  |  \ __ \
 | 
				
			||||
// \    \_\  \  ||  |  \    Y    /  |  / \_\ \
 | 
				
			||||
//  \______  /__||__|   \___|_  /|____/|___  /
 | 
				
			||||
//         \/                 \/           \/
 | 
				
			||||
 | 
				
			||||
type SocialGithub struct { | 
				
			||||
	Token *oauth.Token | 
				
			||||
	*oauth.Transport | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGithub) Type() int { | 
				
			||||
	return models.OT_GITHUB | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newGitHubOauth(config *oauth.Config) { | 
				
			||||
	SocialMap["github"] = &SocialGithub{ | 
				
			||||
		Transport: &oauth.Transport{ | 
				
			||||
			Config:    config, | 
				
			||||
			Transport: http.DefaultTransport, | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGithub) SetRedirectUrl(url string) { | 
				
			||||
	s.Transport.Config.RedirectURL = url | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) { | 
				
			||||
	transport := &oauth.Transport{ | 
				
			||||
		Token: token, | 
				
			||||
	} | 
				
			||||
	var data struct { | 
				
			||||
		Id    int    `json:"id"` | 
				
			||||
		Name  string `json:"login"` | 
				
			||||
		Email string `json:"email"` | 
				
			||||
	} | 
				
			||||
	var err error | 
				
			||||
	r, err := transport.Client().Get(s.Transport.Scope) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	defer r.Body.Close() | 
				
			||||
	if err = json.NewDecoder(r.Body).Decode(&data); err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	return &BasicUserInfo{ | 
				
			||||
		Identity: strconv.Itoa(data.Id), | 
				
			||||
		Name:     data.Name, | 
				
			||||
		Email:    data.Email, | 
				
			||||
	}, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//   ________                     .__
 | 
				
			||||
//  /  _____/  ____   ____   ____ |  |   ____
 | 
				
			||||
// /   \  ___ /  _ \ /  _ \ / ___\|  | _/ __ \
 | 
				
			||||
// \    \_\  (  <_> |  <_> ) /_/  >  |_\  ___/
 | 
				
			||||
//  \______  /\____/ \____/\___  /|____/\___  >
 | 
				
			||||
//         \/             /_____/           \/
 | 
				
			||||
 | 
				
			||||
type SocialGoogle struct { | 
				
			||||
	Token *oauth.Token | 
				
			||||
	*oauth.Transport | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGoogle) Type() int { | 
				
			||||
	return models.OT_GOOGLE | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newGoogleOauth(config *oauth.Config) { | 
				
			||||
	SocialMap["google"] = &SocialGoogle{ | 
				
			||||
		Transport: &oauth.Transport{ | 
				
			||||
			Config:    config, | 
				
			||||
			Transport: http.DefaultTransport, | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGoogle) SetRedirectUrl(url string) { | 
				
			||||
	s.Transport.Config.RedirectURL = url | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) { | 
				
			||||
	transport := &oauth.Transport{Token: token} | 
				
			||||
	var data struct { | 
				
			||||
		Id    string `json:"id"` | 
				
			||||
		Name  string `json:"name"` | 
				
			||||
		Email string `json:"email"` | 
				
			||||
	} | 
				
			||||
	var err error | 
				
			||||
 | 
				
			||||
	reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo" | 
				
			||||
	r, err := transport.Client().Get(reqUrl) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	defer r.Body.Close() | 
				
			||||
	if err = json.NewDecoder(r.Body).Decode(&data); err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	return &BasicUserInfo{ | 
				
			||||
		Identity: data.Id, | 
				
			||||
		Name:     data.Name, | 
				
			||||
		Email:    data.Email, | 
				
			||||
	}, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ________   ________
 | 
				
			||||
// \_____  \  \_____  \
 | 
				
			||||
//  /  / \  \  /  / \  \
 | 
				
			||||
// /   \_/.  \/   \_/.  \
 | 
				
			||||
// \_____\ \_/\_____\ \_/
 | 
				
			||||
//        \__>       \__>
 | 
				
			||||
 | 
				
			||||
type SocialTencent struct { | 
				
			||||
	Token *oauth.Token | 
				
			||||
	*oauth.Transport | 
				
			||||
	reqUrl string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialTencent) Type() int { | 
				
			||||
	return models.OT_QQ | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newTencentOauth(config *oauth.Config) { | 
				
			||||
	SocialMap["qq"] = &SocialTencent{ | 
				
			||||
		reqUrl: "https://open.t.qq.com/api/user/info", | 
				
			||||
		Transport: &oauth.Transport{ | 
				
			||||
			Config:    config, | 
				
			||||
			Transport: http.DefaultTransport, | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialTencent) SetRedirectUrl(url string) { | 
				
			||||
	s.Transport.Config.RedirectURL = url | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserInfo, error) { | 
				
			||||
	var data struct { | 
				
			||||
		Data struct { | 
				
			||||
			Id    string `json:"openid"` | 
				
			||||
			Name  string `json:"name"` | 
				
			||||
			Email string `json:"email"` | 
				
			||||
		} `json:"data"` | 
				
			||||
	} | 
				
			||||
	var err error | 
				
			||||
	// https://open.t.qq.com/api/user/info?
 | 
				
			||||
	//oauth_consumer_key=APP_KEY&
 | 
				
			||||
	//access_token=ACCESSTOKEN&openid=openid
 | 
				
			||||
	//clientip=CLIENTIP&oauth_version=2.a
 | 
				
			||||
	//scope=all
 | 
				
			||||
	var urls = url.Values{ | 
				
			||||
		"oauth_consumer_key": {s.Transport.Config.ClientId}, | 
				
			||||
		"access_token":       {token.AccessToken}, | 
				
			||||
		"openid":             URL.Query()["openid"], | 
				
			||||
		"oauth_version":      {"2.a"}, | 
				
			||||
		"scope":              {"all"}, | 
				
			||||
	} | 
				
			||||
	r, err := http.Get(s.reqUrl + "?" + urls.Encode()) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	defer r.Body.Close() | 
				
			||||
	if err = json.NewDecoder(r.Body).Decode(&data); err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	return &BasicUserInfo{ | 
				
			||||
		Identity: data.Data.Id, | 
				
			||||
		Name:     data.Data.Name, | 
				
			||||
		Email:    data.Data.Email, | 
				
			||||
	}, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ___________       .__  __    __
 | 
				
			||||
// \__    ___/_  _  _|__|/  |__/  |_  ___________
 | 
				
			||||
//   |    |  \ \/ \/ /  \   __\   __\/ __ \_  __ \
 | 
				
			||||
//   |    |   \     /|  ||  |  |  | \  ___/|  | \/
 | 
				
			||||
//   |____|    \/\_/ |__||__|  |__|  \___  >__|
 | 
				
			||||
//                                       \/
 | 
				
			||||
 | 
				
			||||
type SocialTwitter struct { | 
				
			||||
	Token *oauth.Token | 
				
			||||
	*oauth.Transport | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialTwitter) Type() int { | 
				
			||||
	return models.OT_TWITTER | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newTwitterOauth(config *oauth.Config) { | 
				
			||||
	SocialMap["twitter"] = &SocialTwitter{ | 
				
			||||
		Transport: &oauth.Transport{ | 
				
			||||
			Config:    config, | 
				
			||||
			Transport: http.DefaultTransport, | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *SocialTwitter) SetRedirectUrl(url string) { | 
				
			||||
	s.Transport.Config.RedirectURL = url | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//https://github.com/mrjones/oauth
 | 
				
			||||
func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) { | 
				
			||||
	// transport := &oauth.Transport{Token: token}
 | 
				
			||||
	// var data struct {
 | 
				
			||||
	// 	Id    string `json:"id"`
 | 
				
			||||
	// 	Name  string `json:"name"`
 | 
				
			||||
	// 	Email string `json:"email"`
 | 
				
			||||
	// }
 | 
				
			||||
	// var err error
 | 
				
			||||
 | 
				
			||||
	// reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
 | 
				
			||||
	// r, err := transport.Client().Get(reqUrl)
 | 
				
			||||
	// if err != nil {
 | 
				
			||||
	// 	return nil, err
 | 
				
			||||
	// }
 | 
				
			||||
	// defer r.Body.Close()
 | 
				
			||||
	// if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
 | 
				
			||||
	// 	return nil, err
 | 
				
			||||
	// }
 | 
				
			||||
	// return &BasicUserInfo{
 | 
				
			||||
	// 	Identity: data.Id,
 | 
				
			||||
	// 	Name:     data.Name,
 | 
				
			||||
	// 	Email:    data.Email,
 | 
				
			||||
	// }, nil
 | 
				
			||||
	return nil, nil | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue