diff --git a/main.go b/main.go index 6701a5d7c08..ff18e7b2bda 100644 --- a/main.go +++ b/main.go @@ -32,9 +32,9 @@ func main() { app.Usage = "grafana web" app.Version = version app.Commands = []cli.Command{ - cmd.ListAccounts, - cmd.CreateAccount, - cmd.DeleteAccount, + cmd.ListOrgs, + cmd.CreateOrg, + cmd.DeleteOrg, cmd.ImportDashboard, cmd.ListDataSources, cmd.CreateDataSource, diff --git a/pkg/api/account.go b/pkg/api/account.go deleted file mode 100644 index 98fb173af6f..00000000000 --- a/pkg/api/account.go +++ /dev/null @@ -1,50 +0,0 @@ -package api - -import ( - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/middleware" - m "github.com/grafana/grafana/pkg/models" -) - -func GetOrg(c *middleware.Context) { - query := m.GetAccountByIdQuery{Id: c.AccountId} - - if err := bus.Dispatch(&query); err != nil { - if err == m.ErrAccountNotFound { - c.JsonApiErr(404, "Account not found", err) - return - } - - c.JsonApiErr(500, "Failed to get account", err) - return - } - - account := m.AccountDTO{ - Id: query.Result.Id, - Name: query.Result.Name, - } - - c.JSON(200, &account) -} - -func CreateOrg(c *middleware.Context, cmd m.CreateAccountCommand) { - cmd.UserId = c.UserId - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed to create account", err) - return - } - - c.JsonOK("Account created") -} - -func UpdateOrg(c *middleware.Context, cmd m.UpdateAccountCommand) { - cmd.AccountId = c.AccountId - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed to update account", err) - return - } - - c.JsonOK("Account updated") -} diff --git a/pkg/api/api.go b/pkg/api/api.go index 72504e340d6..2d42377ed60 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -46,8 +46,8 @@ func Register(r *macaron.Macaron) { r.Group("/user", func() { r.Get("/", GetUser) r.Put("/", bind(m.UpdateUserCommand{}), UpdateUser) - r.Post("/using/:id", SetUsingAccount) - r.Get("/accounts", GetUserAccounts) + r.Post("/using/:id", UserSetUsingOrg) + r.Get("/orgs", GetUserOrgList) r.Post("/stars/dashboard/:id", StarDashboard) r.Delete("/stars/dashboard/:id", UnstarDashboard) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), ChangeUserPassword) @@ -56,9 +56,9 @@ func Register(r *macaron.Macaron) { // account r.Group("/org", func() { r.Get("/", GetOrg) - r.Post("/", bind(m.CreateAccountCommand{}), CreateOrg) - r.Put("/", bind(m.UpdateAccountCommand{}), UpdateOrg) - r.Post("/users", bind(m.AddAccountUserCommand{}), AddOrgUser) + r.Post("/", bind(m.CreateOrgCommand{}), CreateOrg) + r.Put("/", bind(m.UpdateOrgCommand{}), UpdateOrg) + r.Post("/users", bind(m.AddOrgUserCommand{}), AddOrgUser) r.Get("/users", GetOrgUsers) r.Delete("/users/:id", RemoveOrgUser) }, reqAccountAdmin) diff --git a/pkg/api/apikey.go b/pkg/api/apikey.go index fa0946d0303..74308df9202 100644 --- a/pkg/api/apikey.go +++ b/pkg/api/apikey.go @@ -8,7 +8,7 @@ import ( ) func GetApiKeys(c *middleware.Context) { - query := m.GetApiKeysQuery{AccountId: c.AccountId} + query := m.GetApiKeysQuery{OrgId: c.OrgId} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to list api keys", err) @@ -30,7 +30,7 @@ func GetApiKeys(c *middleware.Context) { func DeleteApiKey(c *middleware.Context) { id := c.ParamsInt64(":id") - cmd := &m.DeleteApiKeyCommand{Id: id, AccountId: c.AccountId} + cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId} err := bus.Dispatch(cmd) if err != nil { @@ -47,7 +47,7 @@ func AddApiKey(c *middleware.Context, cmd m.AddApiKeyCommand) { return } - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId cmd.Key = util.GetRandomString(64) if err := bus.Dispatch(&cmd); err != nil { @@ -71,7 +71,7 @@ func UpdateApiKey(c *middleware.Context, cmd m.UpdateApiKeyCommand) { return } - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId err := bus.Dispatch(&cmd) if err != nil { diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 3fd3932e810..925a790305e 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -29,7 +29,7 @@ func isDasboardStarredByUser(c *middleware.Context, dashId int64) (bool, error) func GetDashboard(c *middleware.Context) { slug := c.Params(":slug") - query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId} + query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId} err := bus.Dispatch(&query) if err != nil { c.JsonApiErr(404, "Dashboard not found", nil) @@ -54,13 +54,13 @@ func GetDashboard(c *middleware.Context) { func DeleteDashboard(c *middleware.Context) { slug := c.Params(":slug") - query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId} + query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(404, "Dashboard not found", nil) return } - cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.AccountId} + cmd := m.DeleteDashboardCommand{Slug: slug, OrgId: c.OrgId} if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "Failed to delete dashboard", err) return @@ -72,7 +72,7 @@ func DeleteDashboard(c *middleware.Context) { } func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) { - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId err := bus.Dispatch(&cmd) if err != nil { diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 631322b93e2..5d9d1ba467e 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -39,8 +39,8 @@ func ProxyDataSourceRequest(c *middleware.Context) { id := c.ParamsInt64(":id") query := m.GetDataSourceByIdQuery{ - Id: id, - AccountId: c.AccountId, + Id: id, + OrgId: c.OrgId, } err := bus.Dispatch(&query) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 33a7bac2667..f4f406504e7 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -8,7 +8,7 @@ import ( ) func GetDataSources(c *middleware.Context) { - query := m.GetDataSourcesQuery{AccountId: c.AccountId} + query := m.GetDataSourcesQuery{OrgId: c.OrgId} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to query datasources", err) @@ -19,7 +19,7 @@ func GetDataSources(c *middleware.Context) { for i, ds := range query.Result { result[i] = &dtos.DataSource{ Id: ds.Id, - AccountId: ds.AccountId, + OrgId: ds.OrgId, Name: ds.Name, Url: ds.Url, Type: ds.Type, @@ -37,8 +37,8 @@ func GetDataSources(c *middleware.Context) { func GetDataSourceById(c *middleware.Context) { query := m.GetDataSourceByIdQuery{ - Id: c.ParamsInt64(":id"), - AccountId: c.AccountId, + Id: c.ParamsInt64(":id"), + OrgId: c.OrgId, } if err := bus.Dispatch(&query); err != nil { @@ -50,7 +50,7 @@ func GetDataSourceById(c *middleware.Context) { c.JSON(200, &dtos.DataSource{ Id: ds.Id, - AccountId: ds.AccountId, + OrgId: ds.OrgId, Name: ds.Name, Url: ds.Url, Type: ds.Type, @@ -71,7 +71,7 @@ func DeleteDataSource(c *middleware.Context) { return } - cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.AccountId} + cmd := &m.DeleteDataSourceCommand{Id: id, OrgId: c.OrgId} err := bus.Dispatch(cmd) if err != nil { @@ -90,7 +90,7 @@ func AddDataSource(c *middleware.Context) { return } - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "Failed to add datasource", err) @@ -108,7 +108,7 @@ func UpdateDataSource(c *middleware.Context) { return } - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId err := bus.Dispatch(&cmd) if err != nil { diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 1a05da32004..efcdd2e1c60 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -19,8 +19,8 @@ type CurrentUser struct { Login string `json:"login"` Email string `json:"email"` Name string `json:"name"` - AccountRole m.RoleType `json:"accountRole"` - AccountName string `json:"accountName"` + OrgRole m.RoleType `json:"orgRole"` + OrgName string `json:"orgName"` IsGrafanaAdmin bool `json:"isGrafanaAdmin"` GravatarUrl string `json:"gravatarUrl"` } @@ -38,7 +38,7 @@ type Dashboard struct { type DataSource struct { Id int64 `json:"id"` - AccountId int64 `json:"accountId"` + OrgId int64 `json:"orgId"` Name string `json:"name"` Type m.DsType `json:"type"` Access m.DsAccess `json:"access"` diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index bc909ca8ba1..9c3cfc7629c 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -10,22 +10,22 @@ import ( ) func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) { - accountDataSources := make([]*m.DataSource, 0) + orgDataSources := make([]*m.DataSource, 0) if c.IsSignedIn { - query := m.GetDataSourcesQuery{AccountId: c.AccountId} + query := m.GetDataSourcesQuery{OrgId: c.OrgId} err := bus.Dispatch(&query) if err != nil { return nil, err } - accountDataSources = query.Result + orgDataSources = query.Result } datasources := make(map[string]interface{}) - for _, ds := range accountDataSources { + for _, ds := range orgDataSources { url := ds.Url if ds.Access == m.DS_ACCESS_PROXY { diff --git a/pkg/api/index.go b/pkg/api/index.go index c63e65e5a39..5cd161bb3d2 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -17,8 +17,8 @@ func setIndexViewData(c *middleware.Context) error { Login: c.Login, Email: c.Email, Name: c.Name, - AccountName: c.AccountName, - AccountRole: c.AccountRole, + OrgName: c.OrgName, + OrgRole: c.OrgRole, GravatarUrl: dtos.GetGravatarUrl(c.Email), IsGrafanaAdmin: c.IsGrafanaAdmin, } diff --git a/pkg/api/org.go b/pkg/api/org.go new file mode 100644 index 00000000000..8b41b0e3f5f --- /dev/null +++ b/pkg/api/org.go @@ -0,0 +1,50 @@ +package api + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/middleware" + m "github.com/grafana/grafana/pkg/models" +) + +func GetOrg(c *middleware.Context) { + query := m.GetOrgByIdQuery{Id: c.OrgId} + + if err := bus.Dispatch(&query); err != nil { + if err == m.ErrOrgNotFound { + c.JsonApiErr(404, "Organization not found", err) + return + } + + c.JsonApiErr(500, "Failed to get organization", err) + return + } + + org := m.OrgDTO{ + Id: query.Result.Id, + Name: query.Result.Name, + } + + c.JSON(200, &org) +} + +func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) { + cmd.UserId = c.UserId + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to create organization", err) + return + } + + c.JsonOK("Organization created") +} + +func UpdateOrg(c *middleware.Context, cmd m.UpdateOrgCommand) { + cmd.OrgId = c.OrgId + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to update organization", err) + return + } + + c.JsonOK("Organization updated") +} diff --git a/pkg/api/account_users.go b/pkg/api/org_users.go similarity index 64% rename from pkg/api/account_users.go rename to pkg/api/org_users.go index 3996220a6ac..123ba08c4c7 100644 --- a/pkg/api/account_users.go +++ b/pkg/api/org_users.go @@ -6,7 +6,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func AddOrgUser(c *middleware.Context, cmd m.AddAccountUserCommand) { +func AddOrgUser(c *middleware.Context, cmd m.AddOrgUserCommand) { if !cmd.Role.IsValid() { c.JsonApiErr(400, "Invalid role specified", nil) return @@ -26,19 +26,19 @@ func AddOrgUser(c *middleware.Context, cmd m.AddAccountUserCommand) { return } - cmd.AccountId = c.AccountId + cmd.OrgId = c.OrgId cmd.UserId = userToAdd.Id if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Could not add user to account", err) + c.JsonApiErr(500, "Could not add user to organization", err) return } - c.JsonOK("User added to account") + c.JsonOK("User added to organization") } func GetOrgUsers(c *middleware.Context) { - query := m.GetAccountUsersQuery{AccountId: c.AccountId} + query := m.GetOrgUsersQuery{OrgId: c.OrgId} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to get account user", err) @@ -51,15 +51,15 @@ func GetOrgUsers(c *middleware.Context) { func RemoveOrgUser(c *middleware.Context) { userId := c.ParamsInt64(":id") - cmd := m.RemoveAccountUserCommand{AccountId: c.AccountId, UserId: userId} + cmd := m.RemoveOrgUserCommand{OrgId: c.OrgId, UserId: userId} if err := bus.Dispatch(&cmd); err != nil { - if err == m.ErrLastAccountAdmin { - c.JsonApiErr(400, "Cannot remove last account admin", nil) + if err == m.ErrLastOrgAdmin { + c.JsonApiErr(400, "Cannot remove last organization admin", nil) return } - c.JsonApiErr(500, "Failed to remove user from account", err) + c.JsonApiErr(500, "Failed to remove user from organization", err) } - c.JsonOK("User removed from account") + c.JsonOK("User removed from organization") } diff --git a/pkg/api/search.go b/pkg/api/search.go index f180482fd0e..c37bba7b669 100644 --- a/pkg/api/search.go +++ b/pkg/api/search.go @@ -44,7 +44,7 @@ func Search(c *middleware.Context) { if tagcloud == "true" { - query := m.GetDashboardTagsQuery{AccountId: c.AccountId} + query := m.GetDashboardTagsQuery{OrgId: c.OrgId} err := bus.Dispatch(&query) if err != nil { c.JsonApiErr(500, "Failed to get tags from database", err) @@ -60,7 +60,7 @@ func Search(c *middleware.Context) { UserId: c.UserId, Limit: limit, IsStarred: starred == "true", - AccountId: c.AccountId, + OrgId: c.OrgId, } err := bus.Dispatch(&query) diff --git a/pkg/api/user.go b/pkg/api/user.go index c7f8214679b..f6fb0e41034 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -11,7 +11,7 @@ func GetUser(c *middleware.Context) { query := m.GetUserInfoQuery{UserId: c.UserId} if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get account", err) + c.JsonApiErr(500, "Failed to get user", err) return } @@ -22,23 +22,23 @@ func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) { cmd.UserId = c.UserId if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(400, "Failed to update account", err) + c.JsonApiErr(400, "Failed to update user", err) return } - c.JsonOK("Account updated") + c.JsonOK("User updated") } -func GetUserAccounts(c *middleware.Context) { - query := m.GetUserAccountsQuery{UserId: c.UserId} +func GetUserOrgList(c *middleware.Context) { + query := m.GetUserOrgListQuery{UserId: c.UserId} if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get user accounts", err) + c.JsonApiErr(500, "Failed to get user organizations", err) return } for _, ac := range query.Result { - if ac.AccountId == c.AccountId { + if ac.OrgId == c.OrgId { ac.IsUsing = true break } @@ -47,17 +47,17 @@ func GetUserAccounts(c *middleware.Context) { c.JSON(200, query.Result) } -func validateUsingAccount(userId int64, accountId int64) bool { - query := m.GetUserAccountsQuery{UserId: userId} +func validateUsingOrg(userId int64, orgId int64) bool { + query := m.GetUserOrgListQuery{UserId: userId} if err := bus.Dispatch(&query); err != nil { return false } - // validate that the account id in the list + // validate that the org id in the list valid := false for _, other := range query.Result { - if other.AccountId == accountId { + if other.OrgId == orgId { valid = true } } @@ -65,25 +65,25 @@ func validateUsingAccount(userId int64, accountId int64) bool { return valid } -func SetUsingAccount(c *middleware.Context) { - usingAccountId := c.ParamsInt64(":id") +func UserSetUsingOrg(c *middleware.Context) { + orgId := c.ParamsInt64(":id") - if !validateUsingAccount(c.UserId, usingAccountId) { - c.JsonApiErr(401, "Not a valid account", nil) + if !validateUsingOrg(c.UserId, orgId) { + c.JsonApiErr(401, "Not a valid organization", nil) return } - cmd := m.SetUsingAccountCommand{ - UserId: c.UserId, - AccountId: usingAccountId, + cmd := m.SetUsingOrgCommand{ + UserId: c.UserId, + OrgId: orgId, } if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed change active account", err) + c.JsonApiErr(500, "Failed change active organization", err) return } - c.JsonOK("Active account changed") + c.JsonOK("Active organization changed") } func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) { diff --git a/pkg/cmd/accounts.go b/pkg/cmd/accounts.go deleted file mode 100644 index aeda7efed9b..00000000000 --- a/pkg/cmd/accounts.go +++ /dev/null @@ -1,99 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/log" - m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" -) - -var ListAccounts = cli.Command{ - Name: "accounts", - Usage: "list accounts", - Description: "Lists the accounts in the system", - Action: listAccounts, -} - -var CreateAccount = cli.Command{ - Name: "accounts:create", - Usage: "create a new account", - Description: "Creates a new account", - Action: createAccount, -} - -var DeleteAccount = cli.Command{ - Name: "accounts:delete", - Usage: "delete an existing account", - Description: "Deletes an existing account", - Action: deleteAccount, -} - -func listAccounts(c *cli.Context) { - initRuntime(c) - - accountsQuery := m.GetAccountsQuery{} - if err := bus.Dispatch(&accountsQuery); err != nil { - log.ConsoleFatalf("Failed to find accounts: %s", err) - } - - w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0) - - fmt.Fprintf(w, "ID\tNAME\n") - for _, account := range accountsQuery.Result { - fmt.Fprintf(w, "%d\t%s\n", account.Id, account.Name) - } - w.Flush() -} - -func createAccount(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Account name arg is required") - } - - name := c.Args().First() - - adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser} - - if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound { - log.ConsoleFatalf("Failed to find default admin user: %s", err) - } - - adminUser := adminQuery.Result - - cmd := m.CreateAccountCommand{Name: name, UserId: adminUser.Id} - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to create account: %s", err) - } - - log.ConsoleInfof("Account %s created for admin user %s\n", name, adminUser.Email) -} - -func deleteAccount(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Account name arg is required") - } - - name := c.Args().First() - accountQuery := m.GetAccountByNameQuery{Name: name} - if err := bus.Dispatch(&accountQuery); err != nil { - log.ConsoleFatalf("Failed to find account: %s", err) - } - - accountId := accountQuery.Result.Id - cmd := m.DeleteAccountCommand{Id: accountId} - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to delete account: %s", err) - } - - log.ConsoleInfof("Account %s deleted", name) -} diff --git a/pkg/cmd/dashboard.go b/pkg/cmd/dashboard.go index 43714eb8e93..04645fb618e 100644 --- a/pkg/cmd/dashboard.go +++ b/pkg/cmd/dashboard.go @@ -41,19 +41,19 @@ func runImport(c *cli.Context) { } if !c.Args().Present() { - log.ConsoleFatal("Account name arg is required") + log.ConsoleFatal("Organization name arg is required") } - accountName := c.Args().First() + orgName := c.Args().First() initRuntime(c) - accountQuery := m.GetAccountByNameQuery{Name: accountName} - if err := bus.Dispatch(&accountQuery); err != nil { + orgQuery := m.GetOrgByNameQuery{Name: orgName} + if err := bus.Dispatch(&orgQuery); err != nil { log.ConsoleFatalf("Failed to find account", err) } - accountId := accountQuery.Result.Id + orgId := orgQuery.Result.Id visitor := func(path string, f os.FileInfo, err error) error { if err != nil { @@ -63,7 +63,7 @@ func runImport(c *cli.Context) { return nil } if strings.HasSuffix(f.Name(), ".json") { - if err := importDashboard(path, accountId); err != nil { + if err := importDashboard(path, orgId); err != nil { log.ConsoleFatalf("Failed to import dashboard file: %v, err: %v", path, err) } } @@ -75,7 +75,7 @@ func runImport(c *cli.Context) { } } -func importDashboard(path string, accountId int64) error { +func importDashboard(path string, orgId int64) error { log.ConsoleInfof("Importing %v", path) reader, err := os.Open(path) @@ -92,7 +92,7 @@ func importDashboard(path string, accountId int64) error { dash.Data["id"] = nil cmd := m.SaveDashboardCommand{ - AccountId: accountId, + OrgId: orgId, Dashboard: dash.Data, } diff --git a/pkg/cmd/datasource.go b/pkg/cmd/datasource.go index ea59a0599ea..072756521fb 100644 --- a/pkg/cmd/datasource.go +++ b/pkg/cmd/datasource.go @@ -81,14 +81,14 @@ func createDataSource(c *cli.Context) { dsAccess := c.String("access") dsDefault := c.Bool("default") - accountQuery := m.GetAccountByNameQuery{Name: name} - if err := bus.Dispatch(&accountQuery); err != nil { - log.ConsoleFatalf("Failed to find account: %s", err) + orgQuery := m.GetOrgByNameQuery{Name: name} + if err := bus.Dispatch(&orgQuery); err != nil { + log.ConsoleFatalf("Failed to find organization: %s", err) } - accountId := accountQuery.Result.Id + orgId := orgQuery.Result.Id - query := m.GetDataSourceByNameQuery{AccountId: accountId, Name: ds} + query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} if err := bus.Dispatch(&query); err != nil { if err != m.ErrDataSourceNotFound { log.ConsoleFatalf("Failed to query for existing datasource: %s", err) @@ -100,7 +100,7 @@ func createDataSource(c *cli.Context) { } cmd := m.AddDataSourceCommand{ - AccountId: accountId, + OrgId: orgId, Name: ds, Url: url, Type: m.DsType(dsType), @@ -135,14 +135,14 @@ func listDatasources(c *cli.Context) { } name := c.Args().First() - accountQuery := m.GetAccountByNameQuery{Name: name} - if err := bus.Dispatch(&accountQuery); err != nil { - log.ConsoleFatalf("Failed to find account: %s", err) + orgQuery := m.GetOrgByNameQuery{Name: name} + if err := bus.Dispatch(&orgQuery); err != nil { + log.ConsoleFatalf("Failed to find organization: %s", err) } - accountId := accountQuery.Result.Id + orgId := orgQuery.Result.Id - query := m.GetDataSourcesQuery{AccountId: accountId} + query := m.GetDataSourcesQuery{OrgId: orgId} if err := bus.Dispatch(&query); err != nil { log.ConsoleFatalf("Failed to find datasources: %s", err) } @@ -161,20 +161,20 @@ func describeDataSource(c *cli.Context) { initRuntime(c) if len(c.Args()) != 2 { - log.ConsoleFatal("Account and datasource name args are required") + log.ConsoleFatal("Organization and datasource name args are required") } name := c.Args().First() ds := c.Args()[1] - accountQuery := m.GetAccountByNameQuery{Name: name} - if err := bus.Dispatch(&accountQuery); err != nil { - log.ConsoleFatalf("Failed to find account: %s", err) + orgQuery := m.GetOrgByNameQuery{Name: name} + if err := bus.Dispatch(&orgQuery); err != nil { + log.ConsoleFatalf("Failed to find organization: %s", err) } - accountId := accountQuery.Result.Id + orgId := orgQuery.Result.Id - query := m.GetDataSourceByNameQuery{AccountId: accountId, Name: ds} + query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} if err := bus.Dispatch(&query); err != nil { log.ConsoleFatalf("Failed to find datasource: %s", err) } @@ -208,20 +208,20 @@ func deleteDataSource(c *cli.Context) { name := c.Args().First() ds := c.Args()[1] - accountQuery := m.GetAccountByNameQuery{Name: name} - if err := bus.Dispatch(&accountQuery); err != nil { - log.ConsoleFatalf("Failed to find account: %s", err) + orgQuery := m.GetOrgByNameQuery{Name: name} + if err := bus.Dispatch(&orgQuery); err != nil { + log.ConsoleFatalf("Failed to find organization: %s", err) } - accountId := accountQuery.Result.Id + orgId := orgQuery.Result.Id - query := m.GetDataSourceByNameQuery{AccountId: accountId, Name: ds} + query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} if err := bus.Dispatch(&query); err != nil { log.ConsoleFatalf("Failed to find datasource: %s", err) } datasource := query.Result - cmd := m.DeleteDataSourceCommand{AccountId: accountId, Id: datasource.Id} + cmd := m.DeleteDataSourceCommand{OrgId: orgId, Id: datasource.Id} if err := bus.Dispatch(&cmd); err != nil { log.ConsoleFatalf("Failed to delete datasource: %s", err) } diff --git a/pkg/cmd/orgs.go b/pkg/cmd/orgs.go new file mode 100644 index 00000000000..bcc2eeec38a --- /dev/null +++ b/pkg/cmd/orgs.go @@ -0,0 +1,99 @@ +package cmd + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/codegangsta/cli" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" +) + +var ListOrgs = cli.Command{ + Name: "orgs", + Usage: "list organizations", + Description: "Lists the organizations in the system", + Action: listOrgs, +} + +var CreateOrg = cli.Command{ + Name: "orgs:create", + Usage: "Creates a new organization", + Description: "Creates a new organization", + Action: createOrg, +} + +var DeleteOrg = cli.Command{ + Name: "orgs:delete", + Usage: "Delete an existing organization", + Description: "Deletes an existing organization", + Action: deleteOrg, +} + +func listOrgs(c *cli.Context) { + initRuntime(c) + + orgsQuery := m.GetOrgListQuery{} + if err := bus.Dispatch(&orgsQuery); err != nil { + log.ConsoleFatalf("Failed to find organizations: %s", err) + } + + w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0) + + fmt.Fprintf(w, "ID\tNAME\n") + for _, org := range orgsQuery.Result { + fmt.Fprintf(w, "%d\t%s\n", org.Id, org.Name) + } + w.Flush() +} + +func createOrg(c *cli.Context) { + initRuntime(c) + + if !c.Args().Present() { + log.ConsoleFatal("Organization name arg is required") + } + + name := c.Args().First() + + adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser} + + if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound { + log.ConsoleFatalf("Failed to find default admin user: %s", err) + } + + adminUser := adminQuery.Result + + cmd := m.CreateOrgCommand{Name: name, UserId: adminUser.Id} + if err := bus.Dispatch(&cmd); err != nil { + log.ConsoleFatalf("Failed to create organization: %s", err) + } + + log.ConsoleInfof("Organization %s created for admin user %s\n", name, adminUser.Email) +} + +func deleteOrg(c *cli.Context) { + initRuntime(c) + + if !c.Args().Present() { + log.ConsoleFatal("Organization name arg is required") + } + + name := c.Args().First() + orgQuery := m.GetOrgByNameQuery{Name: name} + if err := bus.Dispatch(&orgQuery); err != nil { + log.ConsoleFatalf("Failed to find organization: %s", err) + } + + orgId := orgQuery.Result.Id + cmd := m.DeleteOrgCommand{Id: orgId} + if err := bus.Dispatch(&cmd); err != nil { + log.ConsoleFatalf("Failed to delete organization: %s", err) + } + + log.ConsoleInfof("Organization %s deleted", name) +} diff --git a/pkg/events/events.go b/pkg/events/events.go index 2477bbef13e..c3dcac3e2b5 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -50,13 +50,13 @@ func ToOnWriteEvent(event interface{}) (*OnTheWireEvent, error) { return &wireEvent, nil } -type AccountCreated struct { +type OrgCreated struct { Timestamp time.Time `json:"timestamp"` Id int64 `json:"id"` Name string `json:"name"` } -type AccountUpdated struct { +type OrgUpdated struct { Timestamp time.Time `json:"timestamp"` Id int64 `json:"id"` Name string `json:"name"` diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 3a94f2f9bbf..9a86059064d 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -56,7 +56,7 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler { return func(c *Context) { ok := false for _, role := range roles { - if role == c.AccountRole { + if role == c.OrgRole { ok = true break } diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index d6a86de5283..61b0bb570ab 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -56,22 +56,23 @@ func GetContextHandler() macaron.Handler { ctx.SignedInUser = &m.SignedInUser{} // TODO: fix this - ctx.AccountRole = keyInfo.Role + ctx.OrgRole = keyInfo.Role ctx.ApiKeyId = keyInfo.Id - ctx.AccountId = keyInfo.AccountId + ctx.OrgId = keyInfo.OrgId } } else if setting.AnonymousEnabled { - accountQuery := m.GetAccountByNameQuery{Name: setting.AnonymousAccountName} - if err := bus.Dispatch(&accountQuery); err != nil { - if err == m.ErrAccountNotFound { - log.Error(3, "Anonymous access account name does not exist", nil) + orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName} + if err := bus.Dispatch(&orgQuery); err != nil { + if err == m.ErrOrgNotFound { + log.Error(3, "Anonymous access organization name does not exist", nil) } } else { ctx.IsSignedIn = false ctx.HasAnonymousAccess = true ctx.SignedInUser = &m.SignedInUser{} - ctx.AccountRole = m.RoleType(setting.AnonymousAccountRole) - ctx.AccountId = accountQuery.Result.Id + ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole) + ctx.OrgId = orgQuery.Result.Id + ctx.OrgName = orgQuery.Result.Name } } diff --git a/pkg/models/account.go b/pkg/models/account.go deleted file mode 100644 index 3133a3dfdac..00000000000 --- a/pkg/models/account.go +++ /dev/null @@ -1,70 +0,0 @@ -package models - -import ( - "errors" - "time" -) - -// Typed errors -var ( - ErrAccountNotFound = errors.New("Account not found") -) - -type Account struct { - Id int64 - Version int - Name string - Created time.Time - Updated time.Time -} - -// --------------------- -// COMMANDS - -type CreateAccountCommand struct { - Name string `json:"name" binding:"Required"` - - // initial admin user for account - UserId int64 `json:"-"` - Result Account `json:"-"` -} - -type DeleteAccountCommand struct { - Id int64 -} - -type UpdateAccountCommand struct { - Name string `json:"name" binding:"Required"` - AccountId int64 `json:"-"` -} - -type GetUserAccountsQuery struct { - UserId int64 - Result []*UserAccountDTO -} - -type GetAccountByIdQuery struct { - Id int64 - Result *Account -} - -type GetAccountByNameQuery struct { - Name string - Result *Account -} - -type GetAccountsQuery struct { - Result []*Account -} - -type AccountDTO struct { - Id int64 `json:"id"` - Name string `json:"name"` -} - -type UserAccountDTO struct { - AccountId int64 `json:"accountId"` - Name string `json:"name"` - Role RoleType `json:"role"` - IsUsing bool `json:"isUsing"` -} diff --git a/pkg/models/account_user.go b/pkg/models/account_user.go deleted file mode 100644 index de57573262d..00000000000 --- a/pkg/models/account_user.go +++ /dev/null @@ -1,67 +0,0 @@ -package models - -import ( - "errors" - "time" -) - -// Typed errors -var ( - ErrInvalidRoleType = errors.New("Invalid role type") - ErrLastAccountAdmin = errors.New("Cannot remove last account admin") -) - -type RoleType string - -const ( - ROLE_VIEWER RoleType = "Viewer" - ROLE_EDITOR RoleType = "Editor" - ROLE_ADMIN RoleType = "Admin" -) - -func (r RoleType) IsValid() bool { - return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR -} - -type AccountUser struct { - AccountId int64 - UserId int64 - Role RoleType - Created time.Time - Updated time.Time -} - -// --------------------- -// COMMANDS - -type RemoveAccountUserCommand struct { - UserId int64 - AccountId int64 -} - -type AddAccountUserCommand struct { - LoginOrEmail string `json:"loginOrEmail" binding:"Required"` - Role RoleType `json:"role" binding:"Required"` - - AccountId int64 `json:"-"` - UserId int64 `json:"-"` -} - -// ---------------------- -// QUERIES - -type GetAccountUsersQuery struct { - AccountId int64 - Result []*AccountUserDTO -} - -// ---------------------- -// Projections and DTOs - -type AccountUserDTO struct { - AccountId int64 `json:"accountId"` - UserId int64 `json:"userId"` - Email string `json:"email"` - Login string `json:"login"` - Role string `json:"role"` -} diff --git a/pkg/models/apikey.go b/pkg/models/apikey.go index 2b3df37979d..76a84132d0b 100644 --- a/pkg/models/apikey.go +++ b/pkg/models/apikey.go @@ -8,22 +8,22 @@ import ( var ErrInvalidApiKey = errors.New("Invalid API Key") type ApiKey struct { - Id int64 - AccountId int64 - Name string - Key string - Role RoleType - Created time.Time - Updated time.Time + Id int64 + OrgId int64 + Name string + Key string + Role RoleType + Created time.Time + Updated time.Time } // --------------------- // COMMANDS type AddApiKeyCommand struct { - Name string `json:"name" binding:"Required"` - Role RoleType `json:"role" binding:"Required"` - AccountId int64 `json:"-"` - Key string `json:"-"` + Name string `json:"name" binding:"Required"` + Role RoleType `json:"role" binding:"Required"` + OrgId int64 `json:"-"` + Key string `json:"-"` Result *ApiKey `json:"-"` } @@ -33,20 +33,20 @@ type UpdateApiKeyCommand struct { Name string `json:"name"` Role RoleType `json:"role"` - AccountId int64 `json:"-"` + OrgId int64 `json:"-"` } type DeleteApiKeyCommand struct { - Id int64 `json:"id"` - AccountId int64 `json:"-"` + Id int64 `json:"id"` + OrgId int64 `json:"-"` } // ---------------------- // QUERIES type GetApiKeysQuery struct { - AccountId int64 - Result []*ApiKey + OrgId int64 + Result []*ApiKey } type GetApiKeyByKeyQuery struct { diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 0ebde7aa163..a5fd415ac0b 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -14,10 +14,10 @@ var ( ) type Dashboard struct { - Id int64 - Slug string - AccountId int64 - Version int + Id int64 + Slug string + OrgId int64 + Version int Created time.Time Updated time.Time @@ -53,7 +53,7 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard { dash := &Dashboard{} dash.Data = cmd.Dashboard dash.Title = dash.Data["title"].(string) - dash.AccountId = cmd.AccountId + dash.OrgId = cmd.OrgId dash.UpdateSlug() if dash.Data["id"] != nil { @@ -80,14 +80,14 @@ func (dash *Dashboard) UpdateSlug() { type SaveDashboardCommand struct { Dashboard map[string]interface{} `json:"dashboard"` - AccountId int64 `json:"-"` + OrgId int64 `json:"-"` Result *Dashboard } type DeleteDashboardCommand struct { - Slug string - AccountId int64 + Slug string + OrgId int64 } // @@ -95,8 +95,8 @@ type DeleteDashboardCommand struct { // type GetDashboardQuery struct { - Slug string - AccountId int64 + Slug string + OrgId int64 Result *Dashboard } diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 8f1eb730110..e8fe628ec75 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -23,9 +23,9 @@ type DsType string type DsAccess string type DataSource struct { - Id int64 - AccountId int64 - Version int + Id int64 + OrgId int64 + Version int Name string Type DsType @@ -48,7 +48,7 @@ type DataSource struct { // Also acts as api DTO type AddDataSourceCommand struct { - AccountId int64 `json:"-"` + OrgId int64 `json:"-"` Name string Type DsType Access DsAccess @@ -64,7 +64,7 @@ type AddDataSourceCommand struct { // Also acts as api DTO type UpdateDataSourceCommand struct { Id int64 - AccountId int64 + OrgId int64 Name string Type DsType Access DsAccess @@ -76,28 +76,28 @@ type UpdateDataSourceCommand struct { } type DeleteDataSourceCommand struct { - Id int64 - AccountId int64 + Id int64 + OrgId int64 } // --------------------- // QUERIES type GetDataSourcesQuery struct { - AccountId int64 - Result []*DataSource + OrgId int64 + Result []*DataSource } type GetDataSourceByIdQuery struct { - Id int64 - AccountId int64 - Result DataSource + Id int64 + OrgId int64 + Result DataSource } type GetDataSourceByNameQuery struct { - Name string - AccountId int64 - Result DataSource + Name string + OrgId int64 + Result DataSource } // --------------------- diff --git a/pkg/models/org.go b/pkg/models/org.go new file mode 100644 index 00000000000..ab6d97b9ae8 --- /dev/null +++ b/pkg/models/org.go @@ -0,0 +1,65 @@ +package models + +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrOrgNotFound = errors.New("Organization not found") +) + +type Org struct { + Id int64 + Version int + Name string + Created time.Time + Updated time.Time +} + +// --------------------- +// COMMANDS + +type CreateOrgCommand struct { + Name string `json:"name" binding:"Required"` + + // initial admin user for account + UserId int64 `json:"-"` + Result Org `json:"-"` +} + +type DeleteOrgCommand struct { + Id int64 +} + +type UpdateOrgCommand struct { + Name string `json:"name" binding:"Required"` + OrgId int64 `json:"-"` +} + +type GetOrgByIdQuery struct { + Id int64 + Result *Org +} + +type GetOrgByNameQuery struct { + Name string + Result *Org +} + +type GetOrgListQuery struct { + Result []*Org +} + +type OrgDTO struct { + Id int64 `json:"id"` + Name string `json:"name"` +} + +type UserOrgDTO struct { + OrgId int64 `json:"orgId"` + Name string `json:"name"` + Role RoleType `json:"role"` + IsUsing bool `json:"isUsing"` +} diff --git a/pkg/models/org_user.go b/pkg/models/org_user.go new file mode 100644 index 00000000000..811d02e1afe --- /dev/null +++ b/pkg/models/org_user.go @@ -0,0 +1,67 @@ +package models + +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrInvalidRoleType = errors.New("Invalid role type") + ErrLastOrgAdmin = errors.New("Cannot remove last organization admin") +) + +type RoleType string + +const ( + ROLE_VIEWER RoleType = "Viewer" + ROLE_EDITOR RoleType = "Editor" + ROLE_ADMIN RoleType = "Admin" +) + +func (r RoleType) IsValid() bool { + return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR +} + +type OrgUser struct { + OrgId int64 + UserId int64 + Role RoleType + Created time.Time + Updated time.Time +} + +// --------------------- +// COMMANDS + +type RemoveOrgUserCommand struct { + UserId int64 + OrgId int64 +} + +type AddOrgUserCommand struct { + LoginOrEmail string `json:"loginOrEmail" binding:"Required"` + Role RoleType `json:"role" binding:"Required"` + + OrgId int64 `json:"-"` + UserId int64 `json:"-"` +} + +// ---------------------- +// QUERIES + +type GetOrgUsersQuery struct { + OrgId int64 + Result []*OrgUserDTO +} + +// ---------------------- +// Projections and DTOs + +type OrgUserDTO struct { + OrgId int64 `json:"orgId"` + UserId int64 `json:"userId"` + Email string `json:"email"` + Login string `json:"login"` + Role string `json:"role"` +} diff --git a/pkg/models/search.go b/pkg/models/search.go index 0265d48b32d..e5c69f0038e 100644 --- a/pkg/models/search.go +++ b/pkg/models/search.go @@ -22,7 +22,7 @@ type DashboardTagCloudItem struct { type SearchDashboardsQuery struct { Title string Tag string - AccountId int64 + OrgId int64 UserId int64 Limit int IsStarred bool @@ -31,6 +31,6 @@ type SearchDashboardsQuery struct { } type GetDashboardTagsQuery struct { - AccountId int64 - Result []*DashboardTagCloudItem + OrgId int64 + Result []*DashboardTagCloudItem } diff --git a/pkg/models/user.go b/pkg/models/user.go index 2905dce8ba9..3de69f5ac56 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -23,8 +23,8 @@ type User struct { EmailVerified bool Theme string - IsAdmin bool - AccountId int64 + IsAdmin bool + OrgId int64 Created time.Time Updated time.Time @@ -63,9 +63,9 @@ type DeleteUserCommand struct { UserId int64 } -type SetUsingAccountCommand struct { - UserId int64 - AccountId int64 +type SetUsingOrgCommand struct { + UserId int64 + OrgId int64 } // ---------------------- @@ -99,14 +99,19 @@ type SearchUsersQuery struct { Result []*UserSearchHitDTO } +type GetUserOrgListQuery struct { + UserId int64 + Result []*UserOrgDTO +} + // ------------------------ // DTO & Projections type SignedInUser struct { UserId int64 - AccountId int64 - AccountName string - AccountRole RoleType + OrgId int64 + OrgName string + OrgRole RoleType Login string Name string Email string diff --git a/pkg/services/sqlstore/account.go b/pkg/services/sqlstore/account.go deleted file mode 100644 index 7eb6c79fae5..00000000000 --- a/pkg/services/sqlstore/account.go +++ /dev/null @@ -1,135 +0,0 @@ -package sqlstore - -import ( - "time" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/events" - "github.com/grafana/grafana/pkg/log" - m "github.com/grafana/grafana/pkg/models" -) - -func init() { - bus.AddHandler("sql", GetAccountById) - bus.AddHandler("sql", CreateAccount) - bus.AddHandler("sql", SetUsingAccount) - bus.AddHandler("sql", UpdateAccount) - bus.AddHandler("sql", GetAccountByName) - bus.AddHandler("sql", GetAccountsQuery) - bus.AddHandler("sql", DeleteAccount) -} - -func GetAccountsQuery(query *m.GetAccountsQuery) error { - return x.Find(&query.Result) -} - -func GetAccountById(query *m.GetAccountByIdQuery) error { - var account m.Account - exists, err := x.Id(query.Id).Get(&account) - if err != nil { - return err - } - - if !exists { - return m.ErrAccountNotFound - } - - query.Result = &account - return nil -} - -func GetAccountByName(query *m.GetAccountByNameQuery) error { - var account m.Account - exists, err := x.Where("name=?", query.Name).Get(&account) - if err != nil { - return err - } - - if !exists { - return m.ErrAccountNotFound - } - - query.Result = &account - return nil -} - -func CreateAccount(cmd *m.CreateAccountCommand) error { - return inTransaction2(func(sess *session) error { - - account := m.Account{ - Name: cmd.Name, - Created: time.Now(), - Updated: time.Now(), - } - - if _, err := sess.Insert(&account); err != nil { - return err - } - - user := m.AccountUser{ - AccountId: account.Id, - UserId: cmd.UserId, - Role: m.ROLE_ADMIN, - Created: time.Now(), - Updated: time.Now(), - } - - _, err := sess.Insert(&user) - cmd.Result = account - - sess.publishAfterCommit(&events.AccountCreated{ - Timestamp: account.Created, - Id: account.Id, - Name: account.Name, - }) - - return err - }) -} - -func UpdateAccount(cmd *m.UpdateAccountCommand) error { - return inTransaction2(func(sess *session) error { - - account := m.Account{ - Name: cmd.Name, - Updated: time.Now(), - } - - if _, err := sess.Id(cmd.AccountId).Update(&account); err != nil { - return err - } - - sess.publishAfterCommit(&events.AccountUpdated{ - Timestamp: account.Updated, - Id: account.Id, - Name: account.Name, - }) - - return nil - }) -} - -func DeleteAccount(cmd *m.DeleteAccountCommand) error { - return inTransaction2(func(sess *session) error { - - deletes := []string{ - "DELETE FROM star WHERE EXISTS (SELECT 1 FROM dashboard WHERE account_id = ?)", - "DELETE FROM dashboard_tag WHERE EXISTS (SELECT 1 FROM dashboard WHERE account_id = ?)", - "DELETE FROM dashboard WHERE account_id = ?", - "DELETE FROM api_key WHERE account_id = ?", - "DELETE FROM data_source WHERE account_id = ?", - "DELETE FROM account_user WHERE account_id = ?", - "DELETE FROM account WHERE id = ?", - } - - for _, sql := range deletes { - log.Trace(sql) - _, err := sess.Exec(sql, cmd.Id) - if err != nil { - return err - } - } - - return nil - }) -} diff --git a/pkg/services/sqlstore/account_users.go b/pkg/services/sqlstore/account_users.go deleted file mode 100644 index 7aa5de4db3f..00000000000 --- a/pkg/services/sqlstore/account_users.go +++ /dev/null @@ -1,67 +0,0 @@ -package sqlstore - -import ( - "fmt" - "time" - - "github.com/go-xorm/xorm" - - "github.com/grafana/grafana/pkg/bus" - m "github.com/grafana/grafana/pkg/models" -) - -func init() { - bus.AddHandler("sql", AddAccountUser) - bus.AddHandler("sql", RemoveAccountUser) - bus.AddHandler("sql", GetAccountUsers) -} - -func AddAccountUser(cmd *m.AddAccountUserCommand) error { - return inTransaction(func(sess *xorm.Session) error { - - entity := m.AccountUser{ - AccountId: cmd.AccountId, - UserId: cmd.UserId, - Role: cmd.Role, - Created: time.Now(), - Updated: time.Now(), - } - - _, err := sess.Insert(&entity) - return err - }) -} - -func GetAccountUsers(query *m.GetAccountUsersQuery) error { - query.Result = make([]*m.AccountUserDTO, 0) - sess := x.Table("account_user") - sess.Join("INNER", "user", fmt.Sprintf("account_user.user_id=%s.id", x.Dialect().Quote("user"))) - sess.Where("account_user.account_id=?", query.AccountId) - sess.Cols("account_user.account_id", "account_user.user_id", "user.email", "user.login", "account_user.role") - sess.Asc("user.email", "user.login") - - err := sess.Find(&query.Result) - return err -} - -func RemoveAccountUser(cmd *m.RemoveAccountUserCommand) error { - return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM account_user WHERE account_id=? and user_id=?" - _, err := sess.Exec(rawSql, cmd.AccountId, cmd.UserId) - if err != nil { - return err - } - - // validate that there is an admin user left - res, err := sess.Query("SELECT 1 from account_user WHERE account_id=? and role='Admin'", cmd.AccountId) - if err != nil { - return err - } - - if len(res) == 0 { - return m.ErrLastAccountAdmin - } - - return err - }) -} diff --git a/pkg/services/sqlstore/apikey.go b/pkg/services/sqlstore/apikey.go index 5db8586ccb3..d696dbbfa27 100644 --- a/pkg/services/sqlstore/apikey.go +++ b/pkg/services/sqlstore/apikey.go @@ -17,7 +17,7 @@ func init() { } func GetApiKeys(query *m.GetApiKeysQuery) error { - sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name") + sess := x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name") query.Result = make([]*m.ApiKey, 0) return sess.Find(&query.Result) @@ -25,8 +25,8 @@ func GetApiKeys(query *m.GetApiKeysQuery) error { func DeleteApiKey(cmd *m.DeleteApiKeyCommand) error { return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM api_key WHERE id=? and account_id=?" - _, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId) + var rawSql = "DELETE FROM api_key WHERE id=? and org_id=?" + _, err := sess.Exec(rawSql, cmd.Id, cmd.OrgId) return err }) } @@ -34,12 +34,12 @@ func DeleteApiKey(cmd *m.DeleteApiKeyCommand) error { func AddApiKey(cmd *m.AddApiKeyCommand) error { return inTransaction(func(sess *xorm.Session) error { t := m.ApiKey{ - AccountId: cmd.AccountId, - Name: cmd.Name, - Role: cmd.Role, - Key: cmd.Key, - Created: time.Now(), - Updated: time.Now(), + OrgId: cmd.OrgId, + Name: cmd.Name, + Role: cmd.Role, + Key: cmd.Key, + Created: time.Now(), + Updated: time.Now(), } if _, err := sess.Insert(&t); err != nil { @@ -53,13 +53,13 @@ func AddApiKey(cmd *m.AddApiKeyCommand) error { func UpdateApiKey(cmd *m.UpdateApiKeyCommand) error { return inTransaction(func(sess *xorm.Session) error { t := m.ApiKey{ - Id: cmd.Id, - AccountId: cmd.AccountId, - Name: cmd.Name, - Role: cmd.Role, - Updated: time.Now(), + Id: cmd.Id, + OrgId: cmd.OrgId, + Name: cmd.Name, + Role: cmd.Role, + Updated: time.Now(), } - _, err := sess.Where("id=? and account_id=?", t.Id, t.AccountId).Update(&t) + _, err := sess.Where("id=? and org_id=?", t.Id, t.OrgId).Update(&t) return err }) } diff --git a/pkg/services/sqlstore/apikey_test.go b/pkg/services/sqlstore/apikey_test.go index de31b6c1d1d..6f54d6b8627 100644 --- a/pkg/services/sqlstore/apikey_test.go +++ b/pkg/services/sqlstore/apikey_test.go @@ -14,7 +14,7 @@ func TestApiKeyDataAccess(t *testing.T) { InitTestDB(t) Convey("Given saved api key", func() { - cmd := m.AddApiKeyCommand{AccountId: 1, Key: "hello"} + cmd := m.AddApiKeyCommand{OrgId: 1, Key: "hello"} err := AddApiKey(&cmd) So(err, ShouldBeNil) diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 89b7e254fea..4f5cf00d9a1 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -22,7 +22,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { dash := cmd.GetDashboardModel() // try get existing dashboard - existing := m.Dashboard{Slug: dash.Slug, AccountId: dash.AccountId} + existing := m.Dashboard{Slug: dash.Slug, OrgId: dash.OrgId} hasExisting, err := sess.Get(&existing) if err != nil { return err @@ -61,7 +61,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { } func GetDashboard(query *m.GetDashboardQuery) error { - dashboard := m.Dashboard{Slug: query.Slug, AccountId: query.AccountId} + dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId} has, err := x.Get(&dashboard) if err != nil { return err @@ -98,9 +98,9 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error { sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id") } - sql.WriteString(` WHERE dashboard.account_id=?`) + sql.WriteString(` WHERE dashboard.org_id=?`) - params = append(params, query.AccountId) + params = append(params, query.OrgId) if query.IsStarred { sql.WriteString(` AND star.user_id=?`) @@ -158,11 +158,11 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error { term FROM dashboard INNER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id - WHERE dashboard.account_id=? + WHERE dashboard.org_id=? GROUP BY term` query.Result = make([]*m.DashboardTagCloudItem, 0) - sess := x.Sql(sql, query.AccountId) + sess := x.Sql(sql, query.OrgId) err := sess.Find(&query.Result) return err } @@ -171,8 +171,8 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { sess := x.NewSession() defer sess.Close() - rawSql := "DELETE FROM Dashboard WHERE account_id=? and slug=?" - _, err := sess.Exec(rawSql, cmd.AccountId, cmd.Slug) + rawSql := "DELETE FROM Dashboard WHERE org_id=? and slug=?" + _, err := sess.Exec(rawSql, cmd.OrgId, cmd.Slug) return err } diff --git a/pkg/services/sqlstore/dashboard_test.go b/pkg/services/sqlstore/dashboard_test.go index e4830693459..c7a8053d528 100644 --- a/pkg/services/sqlstore/dashboard_test.go +++ b/pkg/services/sqlstore/dashboard_test.go @@ -8,9 +8,9 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func insertTestDashboard(title string, accountId int64, tags ...interface{}) *m.Dashboard { +func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard { cmd := m.SaveDashboardCommand{ - AccountId: accountId, + OrgId: orgId, Dashboard: map[string]interface{}{ "id": nil, "title": title, @@ -40,8 +40,8 @@ func TestDashboardDataAccess(t *testing.T) { Convey("Should be able to get dashboard", func() { query := m.GetDashboardQuery{ - Slug: "test-dash-23", - AccountId: 1, + Slug: "test-dash-23", + OrgId: 1, } err := GetDashboard(&query) @@ -53,8 +53,8 @@ func TestDashboardDataAccess(t *testing.T) { Convey("Should be able to search for dashboard", func() { query := m.SearchDashboardsQuery{ - Title: "test", - AccountId: 1, + Title: "test", + OrgId: 1, } err := SearchDashboards(&query) @@ -66,8 +66,8 @@ func TestDashboardDataAccess(t *testing.T) { }) Convey("Should be able to search for dashboards using tags", func() { - query1 := m.SearchDashboardsQuery{Tag: "webapp", AccountId: 1} - query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", AccountId: 1} + query1 := m.SearchDashboardsQuery{Tag: "webapp", OrgId: 1} + query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", OrgId: 1} err := SearchDashboards(&query1) err = SearchDashboards(&query2) @@ -79,7 +79,7 @@ func TestDashboardDataAccess(t *testing.T) { Convey("Should not be able to save dashboard with same name", func() { cmd := m.SaveDashboardCommand{ - AccountId: 1, + OrgId: 1, Dashboard: map[string]interface{}{ "id": nil, "title": "test dash 23", @@ -92,7 +92,7 @@ func TestDashboardDataAccess(t *testing.T) { }) Convey("Should be able to get dashboard tags", func() { - query := m.GetDashboardTagsQuery{AccountId: 1} + query := m.GetDashboardTagsQuery{OrgId: 1} err := GetDashboardTags(&query) So(err, ShouldBeNil) @@ -113,7 +113,7 @@ func TestDashboardDataAccess(t *testing.T) { }) Convey("Should be able to search for starred dashboards", func() { - query := m.SearchDashboardsQuery{AccountId: 1, UserId: 10, IsStarred: true} + query := m.SearchDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true} err := SearchDashboards(&query) So(err, ShouldBeNil) diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index 12ea001c559..7da46ae72da 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -19,7 +19,7 @@ func init() { } func GetDataSourceById(query *m.GetDataSourceByIdQuery) error { - sess := x.Limit(100, 0).Where("account_id=? AND id=?", query.AccountId, query.Id) + sess := x.Limit(100, 0).Where("org_id=? AND id=?", query.OrgId, query.Id) has, err := sess.Get(&query.Result) if !has { @@ -29,7 +29,7 @@ func GetDataSourceById(query *m.GetDataSourceByIdQuery) error { } func GetDataSourceByName(query *m.GetDataSourceByNameQuery) error { - sess := x.Limit(100, 0).Where("account_id=? AND name=?", query.AccountId, query.Name) + sess := x.Limit(100, 0).Where("org_id=? AND name=?", query.OrgId, query.Name) has, err := sess.Get(&query.Result) if !has { @@ -39,7 +39,7 @@ func GetDataSourceByName(query *m.GetDataSourceByNameQuery) error { } func GetDataSources(query *m.GetDataSourcesQuery) error { - sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name") + sess := x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name") query.Result = make([]*m.DataSource, 0) return sess.Find(&query.Result) @@ -47,8 +47,8 @@ func GetDataSources(query *m.GetDataSourcesQuery) error { func DeleteDataSource(cmd *m.DeleteDataSourceCommand) error { return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM data_source WHERE id=? and account_id=?" - _, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId) + var rawSql = "DELETE FROM data_source WHERE id=? and org_id=?" + _, err := sess.Exec(rawSql, cmd.Id, cmd.OrgId) return err }) } @@ -57,7 +57,7 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { return inTransaction(func(sess *xorm.Session) error { ds := &m.DataSource{ - AccountId: cmd.AccountId, + OrgId: cmd.OrgId, Name: cmd.Name, Type: cmd.Type, Access: cmd.Access, @@ -85,8 +85,8 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { func updateIsDefaultFlag(ds *m.DataSource, sess *xorm.Session) error { // Handle is default flag if ds.IsDefault { - rawSql := "UPDATE data_source SET is_default = 0 WHERE account_id=? AND id <> ?" - if _, err := sess.Exec(rawSql, ds.AccountId, ds.Id); err != nil { + rawSql := "UPDATE data_source SET is_default = 0 WHERE org_id=? AND id <> ?" + if _, err := sess.Exec(rawSql, ds.OrgId, ds.Id); err != nil { return err } } @@ -98,7 +98,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { return inTransaction(func(sess *xorm.Session) error { ds := &m.DataSource{ Id: cmd.Id, - AccountId: cmd.AccountId, + OrgId: cmd.OrgId, Name: cmd.Name, Type: cmd.Type, Access: cmd.Access, @@ -112,7 +112,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { sess.UseBool("is_default") - _, err := sess.Where("id=? and account_id=?", ds.Id, ds.AccountId).Update(ds) + _, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds) if err != nil { return err } diff --git a/pkg/services/sqlstore/datasource_test.go b/pkg/services/sqlstore/datasource_test.go index 17873196d8f..4142602c472 100644 --- a/pkg/services/sqlstore/datasource_test.go +++ b/pkg/services/sqlstore/datasource_test.go @@ -42,16 +42,16 @@ func TestDataAccess(t *testing.T) { Convey("Can add datasource", func() { err := AddDataSource(&m.AddDataSourceCommand{ - AccountId: 10, - Type: m.DS_INFLUXDB, - Access: m.DS_ACCESS_DIRECT, - Url: "http://test", - Database: "site", + OrgId: 10, + Type: m.DS_INFLUXDB, + Access: m.DS_ACCESS_DIRECT, + Url: "http://test", + Database: "site", }) So(err, ShouldBeNil) - query := m.GetDataSourcesQuery{AccountId: 10} + query := m.GetDataSourcesQuery{OrgId: 10} err = GetDataSources(&query) So(err, ShouldBeNil) @@ -59,33 +59,33 @@ func TestDataAccess(t *testing.T) { ds := query.Result[0] - So(ds.AccountId, ShouldEqual, 10) + So(ds.OrgId, ShouldEqual, 10) So(ds.Database, ShouldEqual, "site") }) Convey("Given a datasource", func() { AddDataSource(&m.AddDataSourceCommand{ - AccountId: 10, - Type: m.DS_GRAPHITE, - Access: m.DS_ACCESS_DIRECT, - Url: "http://test", + OrgId: 10, + Type: m.DS_GRAPHITE, + Access: m.DS_ACCESS_DIRECT, + Url: "http://test", }) - query := m.GetDataSourcesQuery{AccountId: 10} + query := m.GetDataSourcesQuery{OrgId: 10} GetDataSources(&query) ds := query.Result[0] Convey("Can delete datasource", func() { - err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: ds.AccountId}) + err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, OrgId: ds.OrgId}) So(err, ShouldBeNil) GetDataSources(&query) So(len(query.Result), ShouldEqual, 0) }) - Convey("Can not delete datasource with wrong accountId", func() { - err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: 123123}) + Convey("Can not delete datasource with wrong orgId", func() { + err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, OrgId: 123123}) So(err, ShouldBeNil) GetDataSources(&query) diff --git a/pkg/services/sqlstore/org.go b/pkg/services/sqlstore/org.go new file mode 100644 index 00000000000..18284feccac --- /dev/null +++ b/pkg/services/sqlstore/org.go @@ -0,0 +1,134 @@ +package sqlstore + +import ( + "time" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/events" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", GetOrgById) + bus.AddHandler("sql", CreateOrg) + bus.AddHandler("sql", UpdateOrg) + bus.AddHandler("sql", GetOrgByName) + bus.AddHandler("sql", GetOrgList) + bus.AddHandler("sql", DeleteOrg) +} + +func GetOrgList(query *m.GetOrgListQuery) error { + return x.Find(&query.Result) +} + +func GetOrgById(query *m.GetOrgByIdQuery) error { + var org m.Org + exists, err := x.Id(query.Id).Get(&org) + if err != nil { + return err + } + + if !exists { + return m.ErrOrgNotFound + } + + query.Result = &org + return nil +} + +func GetOrgByName(query *m.GetOrgByNameQuery) error { + var org m.Org + exists, err := x.Where("name=?", query.Name).Get(&org) + if err != nil { + return err + } + + if !exists { + return m.ErrOrgNotFound + } + + query.Result = &org + return nil +} + +func CreateOrg(cmd *m.CreateOrgCommand) error { + return inTransaction2(func(sess *session) error { + + org := m.Org{ + Name: cmd.Name, + Created: time.Now(), + Updated: time.Now(), + } + + if _, err := sess.Insert(&org); err != nil { + return err + } + + user := m.OrgUser{ + OrgId: org.Id, + UserId: cmd.UserId, + Role: m.ROLE_ADMIN, + Created: time.Now(), + Updated: time.Now(), + } + + _, err := sess.Insert(&user) + cmd.Result = org + + sess.publishAfterCommit(&events.OrgCreated{ + Timestamp: org.Created, + Id: org.Id, + Name: org.Name, + }) + + return err + }) +} + +func UpdateOrg(cmd *m.UpdateOrgCommand) error { + return inTransaction2(func(sess *session) error { + + org := m.Org{ + Name: cmd.Name, + Updated: time.Now(), + } + + if _, err := sess.Id(cmd.OrgId).Update(&org); err != nil { + return err + } + + sess.publishAfterCommit(&events.OrgUpdated{ + Timestamp: org.Updated, + Id: org.Id, + Name: org.Name, + }) + + return nil + }) +} + +func DeleteOrg(cmd *m.DeleteOrgCommand) error { + return inTransaction2(func(sess *session) error { + + deletes := []string{ + "DELETE FROM star WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ?)", + "DELETE FROM dashboard_tag WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ?)", + "DELETE FROM dashboard WHERE org_id = ?", + "DELETE FROM api_key WHERE org_id = ?", + "DELETE FROM data_source WHERE org_id = ?", + "DELETE FROM org_user WHERE org_id = ?", + "DELETE FROM org WHERE id = ?", + } + + for _, sql := range deletes { + log.Trace(sql) + _, err := sess.Exec(sql, cmd.Id) + if err != nil { + return err + } + } + + return nil + }) +} diff --git a/pkg/services/sqlstore/account_test.go b/pkg/services/sqlstore/org_test.go similarity index 92% rename from pkg/services/sqlstore/account_test.go rename to pkg/services/sqlstore/org_test.go index f0c9f5cbd9a..205b907c37c 100644 --- a/pkg/services/sqlstore/account_test.go +++ b/pkg/services/sqlstore/org_test.go @@ -15,11 +15,11 @@ func TestAccountDataAccess(t *testing.T) { InitTestDB(t) Convey("Given single account mode", func() { - setting.SingleAccountMode = true - setting.DefaultAccountName = "test" - setting.DefaultAccountRole = "Viewer" + setting.SingleOrgMode = true + setting.DefaultOrgName = "test" + setting.DefaultOrgRole = "Viewer" - Convey("Users should be added to default account", func() { + Convey("Users should be added to default organization", 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"} @@ -33,15 +33,15 @@ func TestAccountDataAccess(t *testing.T) { GetUserAccounts(&q1) GetUserAccounts(&q2) - So(q1.Result[0].AccountId, ShouldEqual, q2.Result[0].AccountId) + So(q1.Result[0].OrgId, ShouldEqual, q2.Result[0].OrgId) So(q1.Result[0].Role, ShouldEqual, "Viewer") }) }) Convey("Given two saved users", func() { - setting.SingleAccountMode = false + setting.SingleOrgMode = false + setting.DefaultOrgName = "test" - 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/org_users.go b/pkg/services/sqlstore/org_users.go new file mode 100644 index 00000000000..4577eaae89c --- /dev/null +++ b/pkg/services/sqlstore/org_users.go @@ -0,0 +1,67 @@ +package sqlstore + +import ( + "fmt" + "time" + + "github.com/go-xorm/xorm" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", AddOrgUser) + bus.AddHandler("sql", RemoveOrgUser) + bus.AddHandler("sql", GetOrgUsers) +} + +func AddOrgUser(cmd *m.AddOrgUserCommand) error { + return inTransaction(func(sess *xorm.Session) error { + + entity := m.OrgUser{ + OrgId: cmd.OrgId, + UserId: cmd.UserId, + Role: cmd.Role, + Created: time.Now(), + Updated: time.Now(), + } + + _, err := sess.Insert(&entity) + return err + }) +} + +func GetOrgUsers(query *m.GetOrgUsersQuery) error { + query.Result = make([]*m.OrgUserDTO, 0) + sess := x.Table("org_user") + sess.Join("INNER", "user", fmt.Sprintf("account_user.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Where("org_user.org_id=?", query.OrgId) + sess.Cols("org_user.org_id", "org_user.user_id", "user.email", "user.login", "org_user.role") + sess.Asc("user.email", "user.login") + + err := sess.Find(&query.Result) + return err +} + +func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error { + return inTransaction(func(sess *xorm.Session) error { + var rawSql = "DELETE FROM org_user WHERE org_id=? and user_id=?" + _, err := sess.Exec(rawSql, cmd.OrgId, cmd.UserId) + if err != nil { + return err + } + + // validate that there is an admin user left + res, err := sess.Query("SELECT 1 from org_user WHERE org_id=? and role='Admin'", cmd.OrgId) + if err != nil { + return err + } + + if len(res) == 0 { + return m.ErrLastOrgAdmin + } + + return err + }) +} diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index f8773eb26cf..3a06fb205b7 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -20,58 +20,59 @@ func init() { bus.AddHandler("sql", UpdateUser) bus.AddHandler("sql", ChangeUserPassword) bus.AddHandler("sql", GetUserByLogin) - bus.AddHandler("sql", SetUsingAccount) + bus.AddHandler("sql", SetUsingOrg) bus.AddHandler("sql", GetUserInfo) bus.AddHandler("sql", GetSignedInUser) bus.AddHandler("sql", SearchUsers) - bus.AddHandler("sql", GetUserAccounts) + bus.AddHandler("sql", GetUserOrgList) bus.AddHandler("sql", DeleteUser) + bus.AddHandler("sql", SetUsingOrg) } -func getAccountIdForNewUser(userEmail string, sess *session) (int64, error) { - var account m.Account +func getOrgIdForNewUser(userEmail string, sess *session) (int64, error) { + var org m.Org - if setting.SingleAccountMode { - has, err := sess.Where("name=?", setting.DefaultAccountName).Get(&account) + if setting.SingleOrgMode { + has, err := sess.Where("name=?", setting.DefaultOrgName).Get(&org) if err != nil { return 0, err } if has { - return account.Id, nil + return org.Id, nil } else { - account.Name = setting.DefaultAccountName + org.Name = setting.DefaultOrgName } } else { - account.Name = userEmail + org.Name = userEmail } - account.Created = time.Now() - account.Updated = time.Now() + org.Created = time.Now() + org.Updated = time.Now() - if _, err := sess.Insert(&account); err != nil { + if _, err := sess.Insert(&org); err != nil { return 0, err } - return account.Id, nil + return org.Id, nil } func CreateUser(cmd *m.CreateUserCommand) error { return inTransaction2(func(sess *session) error { - accountId, err := getAccountIdForNewUser(cmd.Email, sess) + orgId, err := getOrgIdForNewUser(cmd.Email, sess) if err != nil { return err } // create user user := m.User{ - Email: cmd.Email, - Name: cmd.Name, - Login: cmd.Login, - Company: cmd.Company, - IsAdmin: cmd.IsAdmin, - AccountId: accountId, - Created: time.Now(), - Updated: time.Now(), + Email: cmd.Email, + Name: cmd.Name, + Login: cmd.Login, + Company: cmd.Company, + IsAdmin: cmd.IsAdmin, + OrgId: orgId, + Created: time.Now(), + Updated: time.Now(), } if len(cmd.Password) > 0 { @@ -86,20 +87,20 @@ func CreateUser(cmd *m.CreateUserCommand) error { return err } - // create account user link - accountUser := m.AccountUser{ - AccountId: accountId, - UserId: user.Id, - Role: m.ROLE_ADMIN, - Created: time.Now(), - Updated: time.Now(), + // create org user link + orgUser := m.OrgUser{ + OrgId: orgId, + UserId: user.Id, + Role: m.ROLE_ADMIN, + Created: time.Now(), + Updated: time.Now(), } - if setting.SingleAccountMode && !user.IsAdmin { - accountUser.Role = m.RoleType(setting.DefaultAccountRole) + if setting.SingleOrgMode && !user.IsAdmin { + orgUser.Role = m.RoleType(setting.DefaultOrgRole) } - if _, err = sess.Insert(&accountUser); err != nil { + if _, err = sess.Insert(&orgUser); err != nil { return err } @@ -198,12 +199,12 @@ func ChangeUserPassword(cmd *m.ChangeUserPasswordCommand) error { }) } -func SetUsingAccount(cmd *m.SetUsingAccountCommand) error { +func SetUsingOrg(cmd *m.SetUsingOrgCommand) error { return inTransaction(func(sess *xorm.Session) error { user := m.User{} sess.Id(cmd.UserId).Get(&user) - user.AccountId = cmd.AccountId + user.OrgId = cmd.OrgId _, err := sess.Id(user.Id).Update(&user) return err }) @@ -228,12 +229,12 @@ func GetUserInfo(query *m.GetUserInfoQuery) error { return err } -func GetUserAccounts(query *m.GetUserAccountsQuery) error { - query.Result = make([]*m.UserAccountDTO, 0) - sess := x.Table("account_user") - sess.Join("INNER", "account", "account_user.account_id=account.id") - sess.Where("account_user.user_id=?", query.UserId) - sess.Cols("account.name", "account_user.role", "account_user.account_id") +func GetUserOrgList(query *m.GetUserOrgListQuery) error { + query.Result = make([]*m.UserOrgDTO, 0) + sess := x.Table("org_user") + sess.Join("INNER", "org", "org_user.org_id=org.id") + sess.Where("org_user.user_id=?", query.UserId) + sess.Cols("org.name", "org_user.role", "org_user.account_id") err := sess.Find(&query.Result) return err } @@ -245,12 +246,12 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error { u.email as email, u.login as login, u.name as name, - account.name as account_name, - account_user.role as account_role, - account.id as account_id + org.name as org_name, + org_user.role as org_role, + org.id as org_id FROM ` + dialect.Quote("user") + ` as u - LEFT OUTER JOIN account_user on account_user.account_id = u.account_id and account_user.user_id = u.id - LEFT OUTER JOIN account on account.id = u.account_id + LEFT OUTER JOIN org_user on org_user.org_id = u.org_id and org_user.user_id = u.id + LEFT OUTER JOIN org on org.id = u.org_id WHERE u.id=?` var user m.SignedInUser diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 0212d719e5b..36a7d7fcf1c 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -66,18 +66,18 @@ var ( CookieRememberName string DisableUserSignUp bool - // single account - SingleAccountMode bool - DefaultAccountName string - DefaultAccountRole string + // single organization + SingleOrgMode bool + DefaultOrgName string + DefaultOrgRole string // Http auth AdminUser string AdminPassword string - AnonymousEnabled bool - AnonymousAccountName string - AnonymousAccountRole string + AnonymousEnabled bool + AnonymousOrgName string + AnonymousOrgRole string // Session settings. SessionOptions session.Options @@ -220,14 +220,14 @@ func NewConfigContext(config string) { AdminPassword = security.Key("admin_password").String() // 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"}) + SingleOrgMode = Cfg.Section("organization.single").Key("enabled").MustBool(false) + DefaultOrgName = Cfg.Section("organization.single").Key("org_name").MustString("main") + DefaultOrgRole = Cfg.Section("organization.single").Key("default_role").In("Editor", []string{"Editor", "Admin", "Viewer"}) // anonymous access AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false) - AnonymousAccountName = Cfg.Section("auth.anonymous").Key("account_name").String() - AnonymousAccountRole = Cfg.Section("auth.anonymous").Key("account_role").String() + AnonymousOrgName = Cfg.Section("auth.anonymous").Key("org_name").String() + AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String() // PhantomJS rendering ImagesDir = "data/png"