diff --git a/conf/grafana.ini b/conf/grafana.ini index 324ab39a6f7..3c197462cd7 100644 --- a/conf/grafana.ini +++ b/conf/grafana.ini @@ -40,16 +40,28 @@ session_id_hashkey = admin_user = admin ; default admin password, can be changed before first start of grafana, or in profile settings admin_password = admin -; used for sig -secret_key = !#@FDEWREWR&*( +; used for signing +secret_key = SW2YcwTIb9zpOOhoPsMm ; Auto-login remember days login_remember_days = 7 cookie_username = grafana_user cookie_remember_name = grafana_remember -[auth] -anonymous = false -anonymous_account_id = +[account.single] +; Enable this feature to auto assign new users to a single account, suitable for NON multi tenant setups +enabled = true +; Name of default account +account_name = main +; Default role new users will be automatically assigned +default_role = Editor + +[auth.anonymous] +; enable anonymous access +enabled = false +; specify account name that should be used for unauthenticated users +account = main +; specify role for unauthenticated users +role = Viewer [auth.github] enabled = false diff --git a/pkg/api/signup.go b/pkg/api/signup.go index 786a43d8b7a..0bfde3c155f 100644 --- a/pkg/api/signup.go +++ b/pkg/api/signup.go @@ -4,16 +4,12 @@ import ( "github.com/torkelo/grafana-pro/pkg/bus" "github.com/torkelo/grafana-pro/pkg/middleware" m "github.com/torkelo/grafana-pro/pkg/models" - "github.com/torkelo/grafana-pro/pkg/util" ) -// POST /api/account/signup +// POST /api/user/signup func SignUp(c *middleware.Context, cmd m.CreateUserCommand) { cmd.Login = cmd.Email - cmd.Salt = util.GetRandomString(10) - cmd.Rands = util.GetRandomString(10) - cmd.Password = util.EncodePassword(cmd.Password, cmd.Salt) if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "failed to create user", err) diff --git a/pkg/models/user.go b/pkg/models/user.go index 6efc5dd7b07..822d79187cd 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -36,8 +36,6 @@ type CreateUserCommand struct { Name string `json:"name"` Company string `json:"compay"` Password string `json:"password" binding:"Required"` - Salt string `json:"-"` - Rands string `json:"-"` IsAdmin bool `json:"-"` Result User `json:"-"` diff --git a/pkg/services/sqlstore/account_test.go b/pkg/services/sqlstore/account_test.go index 14019ffa3f9..86c4c7de108 100644 --- a/pkg/services/sqlstore/account_test.go +++ b/pkg/services/sqlstore/account_test.go @@ -6,6 +6,7 @@ import ( . "github.com/smartystreets/goconvey/convey" m "github.com/torkelo/grafana-pro/pkg/models" + "github.com/torkelo/grafana-pro/pkg/setting" ) func TestAccountDataAccess(t *testing.T) { @@ -13,7 +14,34 @@ func TestAccountDataAccess(t *testing.T) { Convey("Testing Account DB Access", t, func() { InitTestDB(t) + Convey("Given single account mode", func() { + setting.SingleAccountMode = true + setting.DefaultAccountName = "test" + setting.DefaultAccountRole = "Viewer" + + Convey("Users should be added to default account", func() { + ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"} + ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"} + + err := CreateUser(&ac1cmd) + So(err, ShouldBeNil) + err = CreateUser(&ac2cmd) + So(err, ShouldBeNil) + + q1 := m.GetUserAccountsQuery{UserId: ac1cmd.Result.Id} + q2 := m.GetUserAccountsQuery{UserId: ac2cmd.Result.Id} + GetUserAccounts(&q1) + GetUserAccounts(&q2) + + So(q1.Result[0].AccountId, ShouldEqual, q2.Result[0].AccountId) + So(q1.Result[0].Role, ShouldEqual, "Viewer") + }) + }) + Convey("Given two saved users", func() { + setting.SingleAccountMode = false + + setting.DefaultAccountName = "test" ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"} ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true} diff --git a/pkg/services/sqlstore/datasource_test.go b/pkg/services/sqlstore/datasource_test.go index 226945bdc83..4d3126e747c 100644 --- a/pkg/services/sqlstore/datasource_test.go +++ b/pkg/services/sqlstore/datasource_test.go @@ -13,6 +13,7 @@ import ( func InitTestDB(t *testing.T) { + t.Log("InitTestDB") x, err := xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr) //x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, sqlutil.TestDB_Mysql.ConnStr) //x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, sqlutil.TestDB_Postgres.ConnStr) diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 9e47776f66c..f3968b4923d 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -11,7 +11,6 @@ import ( m "github.com/torkelo/grafana-pro/pkg/models" "github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrator" "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" @@ -40,9 +39,7 @@ func EnsureAdminUser() { cmd := m.CreateUserCommand{} cmd.Login = setting.AdminUser cmd.Email = setting.AdminUser + "@localhost" - cmd.Salt = util.GetRandomString(10) - cmd.Rands = util.GetRandomString(10) - cmd.Password = util.EncodePassword(setting.AdminPassword, cmd.Salt) + cmd.Password = setting.AdminPassword cmd.IsAdmin = true if err = bus.Dispatch(&cmd); err != nil { diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 78a42bf6e5d..67a78aa3ec6 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -8,6 +8,8 @@ import ( "github.com/torkelo/grafana-pro/pkg/bus" m "github.com/torkelo/grafana-pro/pkg/models" + "github.com/torkelo/grafana-pro/pkg/setting" + "github.com/torkelo/grafana-pro/pkg/util" ) func init() { @@ -21,48 +23,76 @@ func init() { bus.AddHandler("sql", GetUserAccounts) } +func getAccountIdForNewUser(userEmail string, sess *xorm.Session) (int64, error) { + var account m.Account + + if setting.SingleAccountMode { + has, err := sess.Where("name=?", setting.DefaultAccountName).Get(&account) + if err != nil { + return 0, err + } + if has { + return account.Id, nil + } else { + account.Name = setting.DefaultAccountName + } + } else { + account.Name = userEmail + } + + account.Created = time.Now() + account.Updated = time.Now() + + if _, err := sess.Insert(&account); err != nil { + return 0, err + } + + return account.Id, nil +} + func CreateUser(cmd *m.CreateUserCommand) error { return inTransaction(func(sess *xorm.Session) error { - - // create account - account := m.Account{ - Name: cmd.Email, - Created: time.Now(), - Updated: time.Now(), - } - - if _, err := sess.Insert(&account); err != nil { + accountId, err := getAccountIdForNewUser(cmd.Email, sess) + if err != nil { return err } // create user user := m.User{ Email: cmd.Email, - Password: cmd.Password, Name: cmd.Name, Login: cmd.Login, Company: cmd.Company, - Salt: cmd.Salt, - Rands: cmd.Rands, IsAdmin: cmd.IsAdmin, - AccountId: account.Id, + AccountId: accountId, Created: time.Now(), Updated: time.Now(), } + user.Salt = util.GetRandomString(10) + user.Rands = util.GetRandomString(10) + user.Password = util.EncodePassword(cmd.Password, user.Salt) + sess.UseBool("is_admin") + if _, err := sess.Insert(&user); err != nil { return err } // create account user link - _, err := sess.Insert(&m.AccountUser{ - AccountId: account.Id, + accountUser := m.AccountUser{ + AccountId: accountId, UserId: user.Id, Role: m.ROLE_ADMIN, Created: time.Now(), Updated: time.Now(), - }) + } + + if setting.SingleAccountMode { + accountUser.Role = m.RoleType(setting.DefaultAccountRole) + } + + _, err = sess.Insert(&accountUser) cmd.Result = user return err diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 7f90398bada..dcba1afbb23 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -64,6 +64,11 @@ var ( CookieUserName string CookieRememberName string + // single account + SingleAccountMode bool + DefaultAccountName string + DefaultAccountRole string + // Http auth AdminUser string AdminPassword string @@ -190,7 +195,12 @@ func NewConfigContext() { CookieUserName = security.Key("cookie_username").String() CookieRememberName = security.Key("cookie_remember_name").String() - // Http auth + // single account + SingleAccountMode = Cfg.Section("account.single").Key("enabled").MustBool(false) + DefaultAccountName = Cfg.Section("account.single").Key("account_name").MustString("main") + DefaultAccountRole = Cfg.Section("account.single").Key("default_role").In("Editor", []string{"Editor", "Admin", "Viewer"}) + + // admin AdminUser = security.Key("admin_user").String() AdminPassword = security.Key("admin_password").String()