mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Admin flagged users, create a default admin user on startup if missing
This commit is contained in:
parent
5ec07db143
commit
fdfcc3ab2a
@ -35,6 +35,12 @@ session_id_hashfunc = sha1
|
||||
; Session hash key, default is use random string
|
||||
session_id_hashkey =
|
||||
|
||||
[admin]
|
||||
; default admin user, created on startup
|
||||
user = admin
|
||||
; default admin password, can be changed before first start of grafana, or in profile settings
|
||||
password = admin
|
||||
|
||||
[auth]
|
||||
anonymous = false
|
||||
anonymous_account_id =
|
||||
|
2
grafana
2
grafana
@ -1 +1 @@
|
||||
Subproject commit 961ebbde6b6540f03d3fb5a1741722614166099f
|
||||
Subproject commit cf344abff2cdf7638d1748aa698caf23c3848715
|
@ -16,6 +16,7 @@ type LoginResult struct {
|
||||
type CurrentUser struct {
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
|
||||
@ -48,6 +49,7 @@ func NewCurrentUser(account *models.Account) *CurrentUser {
|
||||
model.Login = account.Login
|
||||
model.Email = account.Email
|
||||
model.GravatarUrl = getGravatarUrl(account.Email)
|
||||
model.IsAdmin = account.IsAdmin
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ import (
|
||||
"github.com/torkelo/grafana-pro/pkg/api"
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||
"github.com/torkelo/grafana-pro/pkg/services/sqlstore"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
"github.com/torkelo/grafana-pro/pkg/social"
|
||||
"github.com/torkelo/grafana-pro/pkg/stores/sqlstore"
|
||||
)
|
||||
|
||||
var CmdWeb = cli.Command{
|
||||
@ -76,11 +76,9 @@ func runWeb(c *cli.Context) {
|
||||
log.Info("Version: %v, Commit: %v, Build date: %v", setting.BuildVersion, setting.BuildCommit, time.Unix(setting.BuildStamp, 0))
|
||||
|
||||
setting.NewConfigContext()
|
||||
setting.InitServices()
|
||||
social.NewOAuthService()
|
||||
|
||||
sqlstore.Init()
|
||||
sqlstore.NewEngine()
|
||||
sqlstore.EnsureAdminUser()
|
||||
|
||||
m := newMacaron()
|
||||
api.Register(m)
|
||||
|
@ -31,13 +31,15 @@ type Account struct {
|
||||
// COMMANDS
|
||||
|
||||
type CreateAccountCommand struct {
|
||||
Email string `json:"email" binding:"required"`
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Salt string `json:"-"`
|
||||
Result Account `json:"-"`
|
||||
Email string `json:"email" binding:"required"`
|
||||
Login string `json:"login"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Salt string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
|
||||
Result Account `json:"-"`
|
||||
}
|
||||
|
||||
type SetUsingAccountCommand struct {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
@ -30,10 +31,13 @@ func CreateAccount(cmd *m.CreateAccountCommand) error {
|
||||
Login: cmd.Login,
|
||||
Password: cmd.Password,
|
||||
Salt: cmd.Salt,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
sess.UseBool("is_admin")
|
||||
|
||||
_, err := sess.Insert(&account)
|
||||
cmd.Result = account
|
||||
return err
|
||||
@ -137,10 +141,14 @@ func GetAccountByToken(query *m.GetAccountByTokenQuery) error {
|
||||
}
|
||||
|
||||
func GetAccountByLogin(query *m.GetAccountByLoginQuery) error {
|
||||
var err error
|
||||
account := new(m.Account)
|
||||
if strings.Contains(query.Login, "@") {
|
||||
account = &m.Account{Email: query.Login}
|
||||
} else {
|
||||
account = &m.Account{Login: strings.ToLower(query.Login)}
|
||||
}
|
||||
|
||||
account := m.Account{Login: query.Login}
|
||||
has, err := x.Get(&account)
|
||||
has, err := x.Get(account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -152,7 +160,7 @@ func GetAccountByLogin(query *m.GetAccountByLoginQuery) error {
|
||||
account.UsingAccountId = account.Id
|
||||
}
|
||||
|
||||
query.Result = &account
|
||||
query.Result = account
|
||||
|
||||
return nil
|
||||
}
|
@ -6,9 +6,11 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
"github.com/torkelo/grafana-pro/pkg/util"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/xorm"
|
||||
@ -42,23 +44,39 @@ func init() {
|
||||
new(m.Token))
|
||||
}
|
||||
|
||||
func Init() {
|
||||
func EnsureAdminUser() {
|
||||
adminQuery := m.GetAccountByLoginQuery{Login: setting.AdminUser}
|
||||
|
||||
if err := bus.Dispatch(&adminQuery); err == m.ErrAccountNotFound {
|
||||
cmd := m.CreateAccountCommand{}
|
||||
cmd.Login = setting.AdminUser
|
||||
cmd.Email = setting.AdminUser + "@localhost"
|
||||
cmd.Salt = util.GetRandomString(10)
|
||||
cmd.Password = util.EncodePassword(setting.AdminPassword, cmd.Salt)
|
||||
cmd.IsAdmin = true
|
||||
|
||||
if err = bus.Dispatch(&cmd); err != nil {
|
||||
log.Fatal(3, "Failed to create default admin user", err)
|
||||
}
|
||||
|
||||
log.Info("Created default admin user: %v", setting.AdminUser)
|
||||
} else if err != nil {
|
||||
log.Fatal(3, "Could not determine if admin user exists: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewEngine() (err error) {
|
||||
x, err = getEngine()
|
||||
func NewEngine() {
|
||||
x, err := getEngine()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("sqlstore.init(fail to connect to database): %v", err)
|
||||
log.Fatal(3, "Sqlstore: Fail to connect to database: %v", err)
|
||||
}
|
||||
|
||||
err = SetEngine(x, true)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "fail to initialize orm engine: %v", err)
|
||||
log.Fatal(3, "fail to initialize orm engine: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
|
@ -59,6 +59,8 @@ var (
|
||||
EnableGzip bool
|
||||
|
||||
// Http auth
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
Anonymous bool
|
||||
AnonymousAccountId int64
|
||||
|
||||
@ -119,7 +121,7 @@ func findConfigFiles() []string {
|
||||
func NewConfigContext() {
|
||||
configFiles := findConfigFiles()
|
||||
|
||||
log.Info("Loading config files: %v", configFiles)
|
||||
//log.Info("Loading config files: %v", configFiles)
|
||||
var err error
|
||||
|
||||
Cfg, err = goconfig.LoadConfigFile(configFiles[0])
|
||||
@ -168,6 +170,8 @@ func NewConfigContext() {
|
||||
EnableGzip = Cfg.MustBool("server", "enable_gzip")
|
||||
|
||||
// Http auth
|
||||
AdminUser = Cfg.MustValue("admin", "user", "admin")
|
||||
AdminPassword = Cfg.MustValue("admin", "password", "admin")
|
||||
Anonymous = Cfg.MustBool("auth", "anonymous", false)
|
||||
AnonymousAccountId = Cfg.MustInt64("auth", "anonymous_account_id", 0)
|
||||
|
||||
@ -180,10 +184,11 @@ func NewConfigContext() {
|
||||
PhantomDir = "_vendor/phantomjs"
|
||||
|
||||
LogRootPath = Cfg.MustValue("log", "root_path", path.Join(WorkDir, "/data/log"))
|
||||
|
||||
readSessionConfig()
|
||||
}
|
||||
|
||||
func initSessionService() {
|
||||
|
||||
func readSessionConfig() {
|
||||
SessionOptions = session.Options{}
|
||||
SessionOptions.Provider = Cfg.MustValueRange("session", "provider", "memory", []string{"memory", "file"})
|
||||
SessionOptions.ProviderConfig = strings.Trim(Cfg.MustValue("session", "provider_config"), "\" ")
|
||||
@ -199,7 +204,3 @@ func initSessionService() {
|
||||
|
||||
log.Info("Session Service Enabled")
|
||||
}
|
||||
|
||||
func InitServices() {
|
||||
initSessionService()
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
package stores
|
||||
|
||||
//
|
||||
// import (
|
||||
// "encoding/json"
|
||||
// "io"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
//
|
||||
// log "github.com/alecthomas/log4go"
|
||||
// "github.com/torkelo/grafana-pro/pkg/models"
|
||||
// )
|
||||
//
|
||||
// type fileStore struct {
|
||||
// dataDir string
|
||||
// dashDir string
|
||||
// cache map[string]*models.Dashboard
|
||||
// }
|
||||
//
|
||||
// func NewFileStore(dataDir string) *fileStore {
|
||||
//
|
||||
// if dirDoesNotExist(dataDir) {
|
||||
// log.Crashf("FileStore failed to initialize, dataDir does not exist %v", dataDir)
|
||||
// }
|
||||
//
|
||||
// dashDir := filepath.Join(dataDir, "dashboards")
|
||||
//
|
||||
// if dirDoesNotExist(dashDir) {
|
||||
// log.Debug("Did not find dashboard dir, creating...")
|
||||
// err := os.Mkdir(dashDir, 0777)
|
||||
// if err != nil {
|
||||
// log.Crashf("FileStore failed to initialize, could not create directory %v, error: %v", dashDir, err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// store := &fileStore{}
|
||||
// store.dataDir = dataDir
|
||||
// store.dashDir = dashDir
|
||||
// store.cache = make(map[string]*models.Dashboard)
|
||||
// store.scanFiles()
|
||||
//
|
||||
// return store
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) scanFiles() {
|
||||
// visitor := func(path string, f os.FileInfo, err error) error {
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if f.IsDir() {
|
||||
// return nil
|
||||
// }
|
||||
// if strings.HasSuffix(f.Name(), ".json") {
|
||||
// err = store.loadDashboardIntoCache(path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// err := filepath.Walk(store.dashDir, visitor)
|
||||
// if err != nil {
|
||||
// log.Error("FileStore::updateCache failed %v", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func (store fileStore) loadDashboardIntoCache(filename string) error {
|
||||
// log.Info("Loading dashboard file %v into cache", filename)
|
||||
// dash, err := loadDashboardFromFile(filename)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// store.cache[dash.Title] = dash
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) Close() {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) GetById(id string) (*models.Dashboard, error) {
|
||||
// log.Debug("FileStore::GetById id = %v", id)
|
||||
// filename := store.getFilePathForDashboard(id)
|
||||
//
|
||||
// return loadDashboardFromFile(filename)
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) Save(dash *models.Dashboard) error {
|
||||
// filename := store.getFilePathForDashboard(dash.Title)
|
||||
//
|
||||
// log.Debug("Saving dashboard %v to %v", dash.Title, filename)
|
||||
//
|
||||
// var err error
|
||||
// var data []byte
|
||||
// if data, err = json.Marshal(dash.Data); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// return writeFile(filename, data)
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) Query(query string) ([]*models.SearchResult, error) {
|
||||
// results := make([]*models.SearchResult, 0, 50)
|
||||
//
|
||||
// for _, dash := range store.cache {
|
||||
// item := &models.SearchResult{
|
||||
// Id: dash.Title,
|
||||
// Type: "dashboard",
|
||||
// }
|
||||
// results = append(results, item)
|
||||
// }
|
||||
//
|
||||
// return results, nil
|
||||
// }
|
||||
//
|
||||
// func loadDashboardFromFile(filename string) (*models.Dashboard, error) {
|
||||
// log.Debug("FileStore::loading dashboard from file %v", filename)
|
||||
//
|
||||
// configFile, err := os.Open(filename)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return models.NewFromJson(configFile)
|
||||
// }
|
||||
//
|
||||
// func (store *fileStore) getFilePathForDashboard(id string) string {
|
||||
// id = strings.ToLower(id)
|
||||
// id = strings.Replace(id, " ", "-", -1)
|
||||
// return filepath.Join(store.dashDir, id) + ".json"
|
||||
// }
|
||||
//
|
||||
// func dirDoesNotExist(dir string) bool {
|
||||
// _, err := os.Stat(dir)
|
||||
// return os.IsNotExist(err)
|
||||
// }
|
||||
//
|
||||
// func writeFile(filename string, data []byte) error {
|
||||
// f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// n, err := f.Write(data)
|
||||
// if err == nil && n < len(data) {
|
||||
// err = io.ErrShortWrite
|
||||
// }
|
||||
// if err1 := f.Close(); err == nil {
|
||||
// err = err1
|
||||
// }
|
||||
//
|
||||
// return err
|
||||
// }
|
@ -1,113 +0,0 @@
|
||||
package stores
|
||||
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "io"
|
||||
// "io/ioutil"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "testing"
|
||||
//
|
||||
// . "github.com/smartystreets/goconvey/convey"
|
||||
// "github.com/torkelo/grafana-pro/pkg/models"
|
||||
// )
|
||||
//
|
||||
// func TestFileStore(t *testing.T) {
|
||||
//
|
||||
// GivenFileStore("When saving a dashboard", t, func(store *fileStore) {
|
||||
// dashboard := models.NewDashboard("hello")
|
||||
//
|
||||
// err := store.Save(dashboard)
|
||||
//
|
||||
// Convey("should be saved to disk", func() {
|
||||
// So(err, ShouldBeNil)
|
||||
//
|
||||
// _, err = os.Stat(store.getFilePathForDashboard("hello"))
|
||||
// So(err, ShouldBeNil)
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// GivenFileStore("When getting a saved dashboard", t, func(store *fileStore) {
|
||||
// copyDashboardToTempData("default.json", "", store.dashDir)
|
||||
// dash, err := store.GetById("default")
|
||||
//
|
||||
// Convey("should be read from disk", func() {
|
||||
// So(err, ShouldBeNil)
|
||||
// So(dash, ShouldNotBeNil)
|
||||
//
|
||||
// So(dash.Title, ShouldEqual, "Grafana Play Home")
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// GivenFileStore("when getting dashboard with capital letters", t, func(store *fileStore) {
|
||||
// copyDashboardToTempData("annotations.json", "", store.dashDir)
|
||||
// dash, err := store.GetById("AnnoTations")
|
||||
//
|
||||
// Convey("should be read from disk", func() {
|
||||
// So(err, ShouldBeNil)
|
||||
// So(dash, ShouldNotBeNil)
|
||||
//
|
||||
// So(dash.Title, ShouldEqual, "Annotations")
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// GivenFileStore("When copying dashboards into data dir", t, func(store *fileStore) {
|
||||
// copyDashboardToTempData("annotations.json", "", store.dashDir)
|
||||
// copyDashboardToTempData("default.json", "", store.dashDir)
|
||||
// copyDashboardToTempData("graph-styles.json", "", store.dashDir)
|
||||
// store.scanFiles()
|
||||
//
|
||||
// Convey("scan should generate index of all dashboards", func() {
|
||||
//
|
||||
// result, err := store.Query("*")
|
||||
// So(err, ShouldBeNil)
|
||||
// So(len(result), ShouldEqual, 3)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// func copyDashboardToTempData(name string, destName string, dir string) {
|
||||
// if destName == "" {
|
||||
// destName = name
|
||||
// }
|
||||
// source, _ := filepath.Abs("../../data/dashboards/" + name)
|
||||
// dest := filepath.Join(dir, destName)
|
||||
// err := copyFile(dest, source)
|
||||
// if err != nil {
|
||||
// panic(fmt.Sprintf("failed to copy file %v", name))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func GivenFileStore(desc string, t *testing.T, f func(store *fileStore)) {
|
||||
// Convey(desc, t, func() {
|
||||
// tempDir, _ := ioutil.TempDir("", "store")
|
||||
//
|
||||
// store := NewFileStore(tempDir)
|
||||
//
|
||||
// f(store)
|
||||
//
|
||||
// Reset(func() {
|
||||
// os.RemoveAll(tempDir)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// func copyFile(dst, src string) error {
|
||||
// in, err := os.Open(src)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer in.Close()
|
||||
// out, err := os.Create(dst)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer out.Close()
|
||||
// _, err = io.Copy(out, in)
|
||||
// cerr := out.Close()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return cerr
|
||||
// }
|
Loading…
Reference in New Issue
Block a user