Commit b0b77d66 by Torkel Ödegaard

added gravatar

parent ed8f5dbd
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,
}
......
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"`
}
......
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, "/")
}
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
......
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))
}
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
......
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"})
}
# 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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment