mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
working on oauth
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@ config.js
|
||||
*.sublime-workspace
|
||||
*.swp
|
||||
.idea/
|
||||
|
||||
data/sessions
|
||||
|
||||
BIN
grafana-pro
BIN
grafana-pro
Binary file not shown.
@@ -55,7 +55,7 @@ func (ctx *Context) Handle(status int, title string, err error) {
|
||||
ctx.HTML(status, "index")
|
||||
}
|
||||
|
||||
func (ctx *Context) ApiError(status int, message string, err error) {
|
||||
func (ctx *Context) JsonApiErr(status int, message string, err error) {
|
||||
resp := make(map[string]interface{})
|
||||
|
||||
if err != nil {
|
||||
|
||||
9
pkg/models/models.go
Normal file
9
pkg/models/models.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package models
|
||||
|
||||
type OAuthType int
|
||||
|
||||
const (
|
||||
GITHUB OAuthType = iota + 1
|
||||
GOOGLE
|
||||
TWITTER
|
||||
)
|
||||
@@ -13,7 +13,7 @@ func GetDashboard(c *middleware.Context) {
|
||||
|
||||
dash, err := models.GetDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(404, "Dashboard not found", nil)
|
||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -27,13 +27,13 @@ func DeleteDashboard(c *middleware.Context) {
|
||||
|
||||
dash, err := models.GetDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(404, "Dashboard not found", nil)
|
||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
err = models.DeleteDashboard(slug, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(500, "Failed to delete dashboard", err)
|
||||
c.JsonApiErr(500, "Failed to delete dashboard", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func Search(c *middleware.Context) {
|
||||
|
||||
results, err := models.SearchQuery(query, c.GetAccountId())
|
||||
if err != nil {
|
||||
c.ApiError(500, "Search failed", err)
|
||||
c.JsonApiErr(500, "Search failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func PostDashboard(c *middleware.Context) {
|
||||
var command apimodel.SaveDashboardCommand
|
||||
|
||||
if !c.JsonBody(&command) {
|
||||
c.ApiError(400, "bad request", nil)
|
||||
c.JsonApiErr(400, "bad request", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func PostDashboard(c *middleware.Context) {
|
||||
|
||||
err := models.SaveDashboard(dashboard)
|
||||
if err != nil {
|
||||
c.ApiError(500, "Failed to save dashboard", err)
|
||||
c.JsonApiErr(500, "Failed to save dashboard", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ func Register(m *macaron.Macaron) {
|
||||
m.Post("/logout", login.LogoutPost)
|
||||
m.Post("/login", login.LoginPost)
|
||||
|
||||
// no auth
|
||||
// login
|
||||
m.Get("/login", Index)
|
||||
m.Get("/login/:name", login.OAuthLogin)
|
||||
|
||||
// dashboards
|
||||
m.Get("/dashboard/*", auth, Index)
|
||||
|
||||
71
pkg/routes/login/login_oauth.go
Normal file
71
pkg/routes/login/login_oauth.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
"github.com/torkelo/grafana-pro/pkg/social"
|
||||
)
|
||||
|
||||
func OAuthLogin(ctx *middleware.Context) {
|
||||
if setting.OAuthService == nil {
|
||||
ctx.Handle(404, "social.SocialSignIn(oauth service not enabled)", nil)
|
||||
return
|
||||
}
|
||||
|
||||
name := ctx.Params(":name")
|
||||
connect, ok := social.SocialMap[name]
|
||||
if !ok {
|
||||
ctx.Handle(404, "social.SocialSignIn(social login not enabled)", errors.New(name))
|
||||
return
|
||||
}
|
||||
|
||||
code := ctx.Query("code")
|
||||
if code == "" {
|
||||
ctx.Redirect(connect.AuthCodeURL("", "online", "auto"))
|
||||
return
|
||||
}
|
||||
|
||||
// handle call back
|
||||
transport, err := connect.NewTransportWithCode(code)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "social.SocialSignIn(NewTransportWithCode)", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("social.SocialSignIn(Got token)")
|
||||
|
||||
userInfo, err := connect.UserInfo(transport)
|
||||
if err != nil {
|
||||
ctx.Handle(500, fmt.Sprintf("social.SocialSignIn(get info from %s)", name), err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("social.SocialSignIn(social login): %s", userInfo)
|
||||
|
||||
account, err := models.GetAccountByLogin(userInfo.Email)
|
||||
|
||||
// create account if missing
|
||||
if err == models.ErrAccountNotFound {
|
||||
account = &models.Account{
|
||||
Login: userInfo.Login,
|
||||
Email: userInfo.Email,
|
||||
Name: userInfo.Name,
|
||||
Company: userInfo.Company,
|
||||
}
|
||||
|
||||
if err = models.CreateAccount(account); err != nil {
|
||||
ctx.Handle(500, "Failed to create account", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// login
|
||||
loginUserWithAccount(account, ctx)
|
||||
|
||||
ctx.Redirect("/")
|
||||
}
|
||||
14
pkg/setting/setting_oauth.go
Normal file
14
pkg/setting/setting_oauth.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package setting
|
||||
|
||||
type OAuthInfo struct {
|
||||
ClientId, ClientSecret string
|
||||
Scopes []string
|
||||
AuthUrl, TokenUrl string
|
||||
}
|
||||
|
||||
type OAuther struct {
|
||||
GitHub, Google, Twitter bool
|
||||
OAuthInfos map[string]*OAuthInfo
|
||||
}
|
||||
|
||||
var OAuthService *OAuther
|
||||
175
pkg/social/social.go
Normal file
175
pkg/social/social.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/golang/oauth2"
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
)
|
||||
|
||||
type BasicUserInfo struct {
|
||||
Identity string
|
||||
Name string
|
||||
Email string
|
||||
Login string
|
||||
Company string
|
||||
}
|
||||
|
||||
type SocialConnector interface {
|
||||
Type() int
|
||||
UserInfo(transport *oauth2.Transport) (*BasicUserInfo, error)
|
||||
|
||||
AuthCodeURL(state, accessType, prompt string) string
|
||||
NewTransportWithCode(code string) (*oauth2.Transport, error)
|
||||
}
|
||||
|
||||
var (
|
||||
SocialBaseUrl = "/login"
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
)
|
||||
|
||||
func NewOauthService() {
|
||||
if !setting.Cfg.MustBool("oauth", "enabled") {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
setting.OAuthService = &setting.OAuther{}
|
||||
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
|
||||
|
||||
socialConfigs := make(map[string]*oauth2.Config)
|
||||
|
||||
allOauthes := []string{"github", "google", "twitter"}
|
||||
|
||||
// Load all OAuth config data.
|
||||
for _, name := range allOauthes {
|
||||
info := &setting.OAuthInfo{
|
||||
ClientId: setting.Cfg.MustValue("oauth."+name, "client_id"),
|
||||
ClientSecret: setting.Cfg.MustValue("oauth."+name, "client_secrect"),
|
||||
Scopes: setting.Cfg.MustValueArray("oauth."+name, "scopes", " "),
|
||||
AuthUrl: setting.Cfg.MustValue("oauth."+name, "auth_url"),
|
||||
TokenUrl: setting.Cfg.MustValue("oauth."+name, "token_url"),
|
||||
}
|
||||
|
||||
opts := &oauth2.Options{
|
||||
ClientID: info.ClientId,
|
||||
ClientSecret: info.ClientSecret,
|
||||
RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
|
||||
Scopes: info.Scopes,
|
||||
}
|
||||
|
||||
setting.OAuthService.OAuthInfos[name] = info
|
||||
socialConfigs[name], err = oauth2.NewConfig(opts, info.AuthUrl, info.TokenUrl)
|
||||
if err != nil {
|
||||
log.Error(4, "Failed to init oauth service", err)
|
||||
}
|
||||
}
|
||||
|
||||
enabledOauths := make([]string, 0, 10)
|
||||
|
||||
// GitHub.
|
||||
if setting.Cfg.MustBool("oauth.github", "enabled") {
|
||||
setting.OAuthService.GitHub = true
|
||||
newGitHubOAuth(socialConfigs["github"])
|
||||
enabledOauths = append(enabledOauths, "GitHub")
|
||||
}
|
||||
|
||||
// Google.
|
||||
if setting.Cfg.MustBool("oauth.google", "enabled") {
|
||||
setting.OAuthService.Google = true
|
||||
newGoogleOAuth(socialConfigs["google"])
|
||||
enabledOauths = append(enabledOauths, "Google")
|
||||
}
|
||||
}
|
||||
|
||||
type SocialGithub struct {
|
||||
*oauth2.Config
|
||||
}
|
||||
|
||||
func (s *SocialGithub) Type() int {
|
||||
return int(models.GITHUB)
|
||||
}
|
||||
|
||||
func newGitHubOAuth(config *oauth2.Config) {
|
||||
SocialMap["github"] = &SocialGithub{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialGithub) UserInfo(transport *oauth2.Transport) (*BasicUserInfo, error) {
|
||||
var data struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
var err error
|
||||
client := http.Client{Transport: transport}
|
||||
r, err := client.Get("https://api.github.com/user")
|
||||
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 {
|
||||
*oauth2.Config
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) Type() int {
|
||||
return int(models.GOOGLE)
|
||||
}
|
||||
|
||||
func newGoogleOAuth(config *oauth2.Config) {
|
||||
SocialMap["google"] = &SocialGoogle{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) UserInfo(transport *oauth2.Transport) (*BasicUserInfo, error) {
|
||||
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"
|
||||
client := http.Client{Transport: transport}
|
||||
r, err := 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
|
||||
}
|
||||
0
views/404.html
Normal file
0
views/404.html
Normal file
Reference in New Issue
Block a user