mirror of
https://github.com/grafana/grafana.git
synced 2025-01-26 16:27:02 -06:00
added gravatar
This commit is contained in:
parent
ed8f5dbd22
commit
b0b77d667c
2
grafana
2
grafana
@ -1 +1 @@
|
||||
Subproject commit 5dfeddf583176b52ef36945ec5b6e73a7cdf8646
|
||||
Subproject commit 071ac0dc85e48be546315dde196f90f01ad7b274
|
@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
log "github.com/alecthomas/log4go"
|
||||
"github.com/torkelo/grafana-pro/pkg/configuration"
|
||||
"github.com/torkelo/grafana-pro/pkg/server"
|
||||
)
|
||||
|
||||
@ -16,7 +17,8 @@ func main() {
|
||||
|
||||
log.Info("Starting Grafana-Pro v.1-alpha")
|
||||
|
||||
server, err := server.NewServer(port)
|
||||
cfg := configuration.NewCfg(port)
|
||||
server, err := server.NewServer(cfg)
|
||||
if err != nil {
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/torkelo/grafana-pro/pkg/components"
|
||||
"github.com/torkelo/grafana-pro/pkg/configuration"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
||||
)
|
||||
@ -17,13 +18,15 @@ type HttpServer struct {
|
||||
store stores.Store
|
||||
renderer *components.PhantomRenderer
|
||||
router *gin.Engine
|
||||
cfg *configuration.Cfg
|
||||
}
|
||||
|
||||
var sessionStore = sessions.NewCookieStore([]byte("something-very-secret"))
|
||||
|
||||
func NewHttpServer(port string, store stores.Store) *HttpServer {
|
||||
func NewHttpServer(cfg *configuration.Cfg, store stores.Store) *HttpServer {
|
||||
self := &HttpServer{}
|
||||
self.port = port
|
||||
self.cfg = cfg
|
||||
self.port = cfg.Http.Port
|
||||
self.store = store
|
||||
self.renderer = &components.PhantomRenderer{ImagesDir: "data/png", PhantomDir: "_vendor/phantomjs"}
|
||||
|
||||
@ -63,9 +66,8 @@ func (self *HttpServer) ListenAndServe() {
|
||||
func (self *HttpServer) index(c *gin.Context) {
|
||||
viewModel := &IndexDto{}
|
||||
userAccount, _ := c.Get("userAccount")
|
||||
if userAccount != nil {
|
||||
viewModel.User.Login = userAccount.(*models.Account).Login
|
||||
}
|
||||
account, _ := userAccount.(*models.Account)
|
||||
initCurrentUserDto(&viewModel.User, account)
|
||||
|
||||
c.HTML(200, "index.html", viewModel)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func (self *HttpServer) getAccount(c *gin.Context, auth *authContext) {
|
||||
var account = auth.userAccount
|
||||
|
||||
model := accountInfoDto{
|
||||
Login: account.Login,
|
||||
Name: account.Name,
|
||||
Email: account.Email,
|
||||
AccountName: account.AccountName,
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package api
|
||||
|
||||
type accountInfoDto struct {
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AccountName string `json:"accountName"`
|
||||
Collaborators []*collaboratorInfoDto `json:"collaborators"`
|
||||
}
|
||||
|
111
pkg/api/api_google_oauth.go
Normal file
111
pkg/api/api_google_oauth.go
Normal file
@ -0,0 +1,111 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
log "github.com/alecthomas/log4go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang/oauth2"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
||||
)
|
||||
|
||||
var oauthCfg *oauth2.Config
|
||||
|
||||
func init() {
|
||||
addRoutes(func(self *HttpServer) {
|
||||
if !self.cfg.Http.GoogleOAuth.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
self.router.GET("/login/google", self.loginGoogle)
|
||||
self.router.GET("/oauth2callback", self.oauthCallback)
|
||||
|
||||
options := &oauth2.Options{
|
||||
ClientID: self.cfg.Http.GoogleOAuth.ClientId,
|
||||
ClientSecret: self.cfg.Http.GoogleOAuth.ClientSecret,
|
||||
RedirectURL: "http://localhost:3000/oauth2callback",
|
||||
Scopes: []string{
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := oauth2.NewConfig(options,
|
||||
"https://accounts.google.com/o/oauth2/auth",
|
||||
"https://accounts.google.com/o/oauth2/token")
|
||||
|
||||
if err != nil {
|
||||
log.Error("Failed to init google auth %v", err)
|
||||
}
|
||||
|
||||
oauthCfg = cfg
|
||||
})
|
||||
}
|
||||
|
||||
func (self *HttpServer) loginGoogle(c *gin.Context) {
|
||||
url := oauthCfg.AuthCodeURL("", "online", "auto")
|
||||
c.Redirect(302, url)
|
||||
}
|
||||
|
||||
type googleUserInfoDto struct {
|
||||
Email string `json:"email"`
|
||||
GivenName string `json:"givenName"`
|
||||
FamilyName string `json:"familyName"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (self *HttpServer) oauthCallback(c *gin.Context) {
|
||||
code := c.Request.URL.Query()["code"][0]
|
||||
log.Info("OAuth code: %v", code)
|
||||
|
||||
transport, err := oauthCfg.NewTransportWithCode(code)
|
||||
if err != nil {
|
||||
c.String(500, "Failed to exchange oauth token: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
client := http.Client{Transport: transport}
|
||||
resp, err := client.Get("https://www.googleapis.com/oauth2/v1/userinfo?alt=json")
|
||||
if err != nil {
|
||||
c.String(500, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var userInfo googleUserInfoDto
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
err = decoder.Decode(&userInfo)
|
||||
if err != nil {
|
||||
c.String(500, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(userInfo.Email) < 5 {
|
||||
c.String(500, "Invalid email")
|
||||
return
|
||||
}
|
||||
|
||||
// try find existing account
|
||||
account, err := self.store.GetAccountByLogin(userInfo.Email)
|
||||
|
||||
// create account if missing
|
||||
if err == stores.ErrAccountNotFound {
|
||||
account = &models.Account{
|
||||
Login: userInfo.Email,
|
||||
Email: userInfo.Email,
|
||||
Name: userInfo.Name,
|
||||
}
|
||||
|
||||
if err = self.store.CreateAccount(account); err != nil {
|
||||
log.Error("Failed to create account %v", err)
|
||||
c.String(500, "Failed to create account")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// login
|
||||
loginUserWithAccount(account, c)
|
||||
|
||||
c.Redirect(302, "/")
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
package api
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
|
||||
log "github.com/alecthomas/log4go"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addRoutes(func(self *HttpServer) {
|
||||
self.router.GET("/login/*_", self.index)
|
||||
self.router.GET("/login", self.index)
|
||||
self.router.POST("/login", self.loginPost)
|
||||
self.router.POST("/logout", self.logoutPost)
|
||||
})
|
||||
@ -35,9 +40,7 @@ func (self *HttpServer) loginPost(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
session, _ := sessionStore.Get(c.Request, "grafana-session")
|
||||
session.Values["accountId"] = account.Id
|
||||
session.Save(c.Request, c.Writer)
|
||||
loginUserWithAccount(account, c)
|
||||
|
||||
var resp = &LoginResultDto{}
|
||||
resp.Status = "Logged in"
|
||||
@ -46,6 +49,18 @@ func (self *HttpServer) loginPost(c *gin.Context) {
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func loginUserWithAccount(account *models.Account, c *gin.Context) {
|
||||
if account == nil {
|
||||
log.Error("Account login with nil account")
|
||||
}
|
||||
session, err := sessionStore.Get(c.Request, "grafana-session")
|
||||
if err != nil {
|
||||
log.Error("Failed to get session %v", err)
|
||||
}
|
||||
session.Values["accountId"] = account.Id
|
||||
session.Save(c.Request, c.Writer)
|
||||
}
|
||||
|
||||
func (self *HttpServer) logoutPost(c *gin.Context) {
|
||||
session, _ := sessionStore.Get(c.Request, "grafana-session")
|
||||
session.Values = nil
|
||||
|
@ -1,5 +1,13 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
type saveDashboardCommand struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
@ -15,7 +23,9 @@ type IndexDto struct {
|
||||
}
|
||||
|
||||
type CurrentUserDto struct {
|
||||
Login string `json:"login"`
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
|
||||
type LoginResultDto struct {
|
||||
@ -26,3 +36,17 @@ type LoginResultDto struct {
|
||||
func newErrorResponse(message string) *errorResponse {
|
||||
return &errorResponse{Message: message}
|
||||
}
|
||||
|
||||
func initCurrentUserDto(userDto *CurrentUserDto, account *models.Account) {
|
||||
if account != nil {
|
||||
userDto.Login = account.Login
|
||||
userDto.Email = account.Email
|
||||
userDto.GravatarUrl = getGravatarUrl(account.Email)
|
||||
}
|
||||
}
|
||||
|
||||
func getGravatarUrl(text string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(strings.ToLower(text)))
|
||||
return fmt.Sprintf("https://secure.gravatar.com/avatar/%x?s=90&default=mm", hasher.Sum(nil))
|
||||
}
|
||||
|
@ -1,11 +1,34 @@
|
||||
package configuration
|
||||
|
||||
type Cfg struct {
|
||||
httpPort string
|
||||
DashboardSource DashboardSourceCfg
|
||||
Http HttpCfg
|
||||
}
|
||||
|
||||
type HttpCfg struct {
|
||||
Port string
|
||||
GoogleOAuth GoogleOAuthCfg
|
||||
}
|
||||
|
||||
type GoogleOAuthCfg struct {
|
||||
Enabled bool
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
type DashboardSourceCfg struct {
|
||||
sourceType string
|
||||
path string
|
||||
}
|
||||
|
||||
func NewCfg(port string) *Cfg {
|
||||
return &Cfg{
|
||||
Http: HttpCfg{
|
||||
Port: port,
|
||||
GoogleOAuth: GoogleOAuthCfg{
|
||||
Enabled: true,
|
||||
ClientId: "106011922963-4pvl05e9urtrm8bbqr0vouosj3e8p8kb.apps.googleusercontent.com",
|
||||
ClientSecret: "K2evIa4QhfbhhAm3SO72t2Zv",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ type Account struct {
|
||||
Email string
|
||||
AccountName string
|
||||
Password string
|
||||
Name string
|
||||
NextDashboardId int
|
||||
UsingAccountId int
|
||||
Collaborators []CollaboratorLink
|
||||
|
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/torkelo/grafana-pro/pkg/api"
|
||||
"github.com/torkelo/grafana-pro/pkg/configuration"
|
||||
"github.com/torkelo/grafana-pro/pkg/stores"
|
||||
)
|
||||
|
||||
@ -10,9 +11,10 @@ type Server struct {
|
||||
Store stores.Store
|
||||
}
|
||||
|
||||
func NewServer(port string) (*Server, error) {
|
||||
func NewServer(cfg *configuration.Cfg) (*Server, error) {
|
||||
store := stores.New()
|
||||
httpServer := api.NewHttpServer(port, store)
|
||||
|
||||
httpServer := api.NewHttpServer(cfg, store)
|
||||
|
||||
return &Server{
|
||||
HttpServer: httpServer,
|
||||
|
@ -56,7 +56,7 @@ func (self *rethinkStore) GetAccountByLogin(emailOrName string) (*models.Account
|
||||
var account models.Account
|
||||
err = resp.One(&account)
|
||||
if err != nil {
|
||||
return nil, errors.New("Not found")
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
|
@ -1,6 +1,8 @@
|
||||
package stores
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
@ -17,6 +19,11 @@ type Store interface {
|
||||
Close()
|
||||
}
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrAccountNotFound = errors.New("Account not found")
|
||||
)
|
||||
|
||||
func New() Store {
|
||||
return NewRethinkStore(&RethinkCfg{DatabaseName: "grafana"})
|
||||
}
|
||||
|
4
todo.txt
Normal file
4
todo.txt
Normal file
@ -0,0 +1,4 @@
|
||||
# Security
|
||||
- OAuth and email, unique?
|
||||
- Form authentication token
|
||||
- Password hash
|
@ -46,9 +46,7 @@
|
||||
|
||||
<script>
|
||||
window.grafanaBootData = {
|
||||
user: {
|
||||
login: [[.User.Login]]
|
||||
}
|
||||
user:[[.User]]
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user