diff --git a/pkg/api/account.go b/pkg/api/account.go deleted file mode 100644 index 3085415deb7..00000000000 --- a/pkg/api/account.go +++ /dev/null @@ -1,96 +0,0 @@ -package api - -import ( - "github.com/torkelo/grafana-pro/pkg/bus" - "github.com/torkelo/grafana-pro/pkg/middleware" - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func GetAccount(c *middleware.Context) { - query := m.GetAccountInfoQuery{Id: c.AccountId} - - if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to fetch collaboratos", err) - return - } - - c.JSON(200, query.Result) -} - -func UpdateAccount(c *middleware.Context, cmd m.UpdateAccountCommand) { - cmd.AccountId = c.AccountId - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(400, "Failed to update account", nil) - return - } - - c.JsonOK("Account updated") -} - -func GetOtherAccounts(c *middleware.Context) { - query := m.GetOtherAccountsQuery{AccountId: c.AccountId} - - if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get other accounts", err) - return - } - - result := append(query.Result, &m.OtherAccountDTO{ - AccountId: c.AccountId, - Role: m.ROLE_OWNER, - Email: c.UserEmail, - }) - - for _, ac := range result { - if ac.AccountId == c.UsingAccountId { - ac.IsUsing = true - break - } - } - - c.JSON(200, result) -} - -func validateUsingAccount(accountId int64, otherId int64) bool { - if accountId == otherId { - return true - } - - query := m.GetOtherAccountsQuery{AccountId: accountId} - err := bus.Dispatch(&query) - if err != nil { - return false - } - - // validate that the account id in the list - valid := false - for _, other := range query.Result { - if other.AccountId == otherId { - valid = true - } - } - return valid -} - -func SetUsingAccount(c *middleware.Context) { - usingAccountId := c.ParamsInt64(":id") - - if !validateUsingAccount(c.AccountId, usingAccountId) { - c.JsonApiErr(401, "Not a valid account", nil) - return - } - - cmd := m.SetUsingAccountCommand{ - AccountId: c.AccountId, - UsingAccountId: usingAccountId, - } - - err := bus.Dispatch(&cmd) - if err != nil { - c.JsonApiErr(500, "Failed to update account", err) - return - } - - c.JsonOK("Active account changed") -} diff --git a/pkg/api/account_users.go b/pkg/api/account_users.go new file mode 100644 index 00000000000..8c0fc61d308 --- /dev/null +++ b/pkg/api/account_users.go @@ -0,0 +1,61 @@ +package api + +import ( + "github.com/torkelo/grafana-pro/pkg/bus" + "github.com/torkelo/grafana-pro/pkg/middleware" + m "github.com/torkelo/grafana-pro/pkg/models" +) + +func AddAccountUser(c *middleware.Context, cmd m.AddAccountUserCommand) { + if !cmd.Role.IsValid() { + c.JsonApiErr(400, "Invalid role specified", nil) + return + } + + userQuery := m.GetUserByLoginQuery{LoginOrEmail: cmd.LoginOrEmail} + err := bus.Dispatch(&userQuery) + if err != nil { + c.JsonApiErr(404, "User not found", nil) + return + } + + userToAdd := userQuery.Result + + if userToAdd.Id == c.UserId { + c.JsonApiErr(400, "Cannot add yourself as user", nil) + return + } + + cmd.AccountId = c.AccountId + cmd.UserId = userToAdd.Id + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Could not add user to account", err) + return + } + + c.JsonOK("User added to account") +} + +func GetAccountUsers(c *middleware.Context) { + query := m.GetAccountUsersQuery{AccountId: c.AccountId} + + if err := bus.Dispatch(&query); err != nil { + c.JsonApiErr(500, "Failed to get account user", err) + return + } + + c.JSON(200, query.Result) +} + +func RemoveAccountUser(c *middleware.Context) { + userId := c.ParamsInt64(":id") + + cmd := m.RemoveAccountUserCommand{AccountId: c.AccountId, UserId: userId} + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to remove user from account", err) + } + + c.JsonOK("User removed from account") +} diff --git a/pkg/api/admin_accounts.go b/pkg/api/admin_users.go similarity index 76% rename from pkg/api/admin_accounts.go rename to pkg/api/admin_users.go index f897930ea7e..3c766ab0af0 100644 --- a/pkg/api/admin_accounts.go +++ b/pkg/api/admin_users.go @@ -6,11 +6,11 @@ import ( m "github.com/torkelo/grafana-pro/pkg/models" ) -func AdminSearchAccounts(c *middleware.Context) { +func AdminSearchUsers(c *middleware.Context) { // query := c.QueryStrings("q") // page := c.QueryStrings("p") - query := m.SearchAccountsQuery{Query: "", Page: 0, Limit: 20} + query := m.SearchUsersQuery{Query: "", Page: 0, Limit: 20} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(500, "Failed to fetch collaboratos", err) return diff --git a/pkg/api/api.go b/pkg/api/api.go index 3bcab2a904f..3b4df8c3b39 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -13,7 +13,7 @@ import ( func Register(r *macaron.Macaron) { reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true}) reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true}) - reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_OWNER) + reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN) bind := binding.Bind // not logged in views @@ -24,6 +24,7 @@ func Register(r *macaron.Macaron) { r.Get("/login", Index) // authed views + r.Get("/user/", reqSignedIn, Index) r.Get("/account/", reqSignedIn, Index) r.Get("/account/datasources/", reqSignedIn, Index) r.Get("/account/collaborators/", reqSignedIn, Index) @@ -38,15 +39,21 @@ func Register(r *macaron.Macaron) { // authed api r.Group("/api", func() { + // user + r.Group("/user", func() { + r.Get("/", GetUser) + r.Post("/", bind(m.UpdateUserCommand{}), UpdateUser) + r.Post("/using/:id", SetUsingAccount) + r.Get("/accounts", GetUserAccounts) + }) + // account r.Group("/account", func() { - r.Get("/", GetAccount) - r.Post("/", bind(m.UpdateAccountCommand{}), UpdateAccount) - r.Put("/collaborators", bind(m.AddCollaboratorCommand{}), AddCollaborator) - r.Get("/collaborators", GetCollaborators) - r.Delete("/collaborators/:id", RemoveCollaborator) - r.Post("/using/:id", SetUsingAccount) - r.Get("/others", GetOtherAccounts) + //r.Get("/", GetAccount) + //r.Post("/", bind(m.UpdateAccountCommand{}), UpdateAccount) + r.Put("/users", bind(m.AddAccountUserCommand{}), AddAccountUser) + r.Get("/users", GetAccountUsers) + r.Delete("/users/:id", RemoveAccountUser) }) // Token r.Group("/tokens", func() { @@ -75,7 +82,7 @@ func Register(r *macaron.Macaron) { // admin api r.Group("/api/admin", func() { - r.Get("/accounts", AdminSearchAccounts) + r.Get("/users", AdminSearchUsers) }, reqGrafanaAdmin) // rendering @@ -94,13 +101,13 @@ func setIndexViewData(c *middleware.Context) error { if c.IsSignedIn { currentUser = &dtos.CurrentUser{ - Login: c.UserLogin, - Email: c.UserEmail, - Name: c.UserName, - UsingAccountName: c.UsingAccountName, - GravatarUrl: dtos.GetGravatarUrl(c.UserEmail), + Login: c.Login, + Email: c.Email, + Name: c.Name, + UsingAccountName: c.AccountName, + GravatarUrl: dtos.GetGravatarUrl(c.Email), IsGrafanaAdmin: c.IsGrafanaAdmin, - Role: c.UserRole, + Role: c.AccountRole, } } diff --git a/pkg/api/collaborators.go b/pkg/api/collaborators.go deleted file mode 100644 index 891f8c34e46..00000000000 --- a/pkg/api/collaborators.go +++ /dev/null @@ -1,61 +0,0 @@ -package api - -import ( - "github.com/torkelo/grafana-pro/pkg/bus" - "github.com/torkelo/grafana-pro/pkg/middleware" - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func AddCollaborator(c *middleware.Context, cmd m.AddCollaboratorCommand) { - if !cmd.Role.IsValid() { - c.JsonApiErr(400, "Invalid role specified", nil) - return - } - - userQuery := m.GetAccountByLoginQuery{LoginOrEmail: cmd.LoginOrEmail} - err := bus.Dispatch(&userQuery) - if err != nil { - c.JsonApiErr(404, "Collaborator not found", nil) - return - } - - accountToAdd := userQuery.Result - - if accountToAdd.Id == c.AccountId { - c.JsonApiErr(400, "Cannot add yourself as collaborator", nil) - return - } - - cmd.AccountId = c.AccountId - cmd.CollaboratorId = accountToAdd.Id - - err = bus.Dispatch(&cmd) - if err != nil { - c.JsonApiErr(500, "Could not add collaborator", err) - return - } - - c.JsonOK("Collaborator added") -} - -func GetCollaborators(c *middleware.Context) { - query := m.GetCollaboratorsQuery{AccountId: c.AccountId} - if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get collaborators", err) - return - } - - c.JSON(200, query.Result) -} - -func RemoveCollaborator(c *middleware.Context) { - collaboratorId := c.ParamsInt64(":id") - - cmd := m.RemoveCollaboratorCommand{AccountId: c.AccountId, CollaboratorId: collaboratorId} - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed to remove collaborator", err) - } - - c.JsonOK("Collaborator removed") -} diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index d4943964e24..e505179f149 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -10,7 +10,7 @@ import ( func GetDashboard(c *middleware.Context) { slug := c.Params(":slug") - query := m.GetDashboardQuery{Slug: slug, AccountId: c.UsingAccountId} + query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId} err := bus.Dispatch(&query) if err != nil { c.JsonApiErr(404, "Dashboard not found", nil) @@ -25,13 +25,13 @@ func GetDashboard(c *middleware.Context) { func DeleteDashboard(c *middleware.Context) { slug := c.Params(":slug") - query := m.GetDashboardQuery{Slug: slug, AccountId: c.UsingAccountId} + query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId} if err := bus.Dispatch(&query); err != nil { c.JsonApiErr(404, "Dashboard not found", nil) return } - cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.UsingAccountId} + cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.AccountId} if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "Failed to delete dashboard", err) return @@ -43,7 +43,7 @@ func DeleteDashboard(c *middleware.Context) { } func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) { - cmd.AccountId = c.UsingAccountId + cmd.AccountId = c.AccountId err := bus.Dispatch(&cmd) if err != nil { diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 7b4d1070fd0..9849b96c5fe 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -39,7 +39,7 @@ func ProxyDataSourceRequest(c *middleware.Context) { query := m.GetDataSourceByIdQuery{ Id: id, - AccountId: c.UsingAccountId, + AccountId: c.AccountId, } err := bus.Dispatch(&query) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index e363e9eb2be..7b3d95abac9 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.UsingAccountId} + query := m.GetDataSourcesQuery{AccountId: c.AccountId} err := bus.Dispatch(&query) if err != nil { @@ -44,7 +44,7 @@ func DeleteDataSource(c *middleware.Context) { return } - cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.UsingAccountId} + cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.AccountId} err := bus.Dispatch(cmd) if err != nil { @@ -63,7 +63,7 @@ func AddDataSource(c *middleware.Context) { return } - cmd.AccountId = c.UsingAccountId + cmd.AccountId = c.AccountId if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "Failed to add datasource", err) @@ -81,7 +81,7 @@ func UpdateDataSource(c *middleware.Context) { return } - cmd.AccountId = c.UsingAccountId + cmd.AccountId = c.AccountId err := bus.Dispatch(&cmd) if err != nil { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 2159c2386fb..4860f035b9c 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -13,7 +13,7 @@ func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error) accountDataSources := make([]*m.DataSource, 0) if c.IsSignedIn { - query := m.GetDataSourcesQuery{AccountId: c.UsingAccountId} + query := m.GetDataSourcesQuery{AccountId: c.AccountId} err := bus.Dispatch(&query) if err != nil { diff --git a/pkg/api/login.go b/pkg/api/login.go index 5a478ea13c2..42e35f107cd 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -23,7 +23,7 @@ func LoginPost(c *middleware.Context) { return } - userQuery := m.GetAccountByLoginQuery{LoginOrEmail: loginModel.Email} + userQuery := m.GetUserByLoginQuery{LoginOrEmail: loginModel.Email} err := bus.Dispatch(&userQuery) if err != nil { @@ -31,32 +31,32 @@ func LoginPost(c *middleware.Context) { return } - account := userQuery.Result + user := userQuery.Result - passwordHashed := util.EncodePassword(loginModel.Password, account.Salt) - if passwordHashed != account.Password { + passwordHashed := util.EncodePassword(loginModel.Password, user.Salt) + if passwordHashed != user.Password { c.JsonApiErr(401, "Invalid username or password", err) return } - loginUserWithAccount(account, c) + loginUserWithUser(user, c) var resp = &dtos.LoginResult{} resp.Status = "Logged in" - resp.User.Login = account.Login + resp.User.Login = user.Login c.JSON(200, resp) } -func loginUserWithAccount(account *m.Account, c *middleware.Context) { - if account == nil { - log.Error(3, "Account login with nil account") +func loginUserWithUser(user *m.User, c *middleware.Context) { + if user == nil { + log.Error(3, "User login with nil user") } - c.Session.Set("accountId", account.Id) + c.Session.Set("userId", user.Id) } func LogoutPost(c *middleware.Context) { - c.Session.Delete("accountId") + c.Session.Delete("userId") c.JSON(200, util.DynMap{"status": "logged out"}) } diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 6dcba373eac..3d1bca68d5f 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -51,12 +51,12 @@ func OAuthLogin(ctx *middleware.Context) { log.Info("login.OAuthLogin(social login): %s", userInfo) - userQuery := m.GetAccountByLoginQuery{LoginOrEmail: userInfo.Email} + userQuery := m.GetUserByLoginQuery{LoginOrEmail: userInfo.Email} err = bus.Dispatch(&userQuery) // create account if missing - if err == m.ErrAccountNotFound { - cmd := m.CreateAccountCommand{ + if err == m.ErrUserNotFound { + cmd := m.CreateUserCommand{ Login: userInfo.Email, Email: userInfo.Email, Name: userInfo.Name, @@ -74,7 +74,7 @@ func OAuthLogin(ctx *middleware.Context) { } // login - loginUserWithAccount(userQuery.Result, ctx) + loginUserWithUser(userQuery.Result, ctx) ctx.Redirect(setting.AppSubUrl + "/") } diff --git a/pkg/api/render.go b/pkg/api/render.go index d6877679f2f..5f7f6aeff49 100644 --- a/pkg/api/render.go +++ b/pkg/api/render.go @@ -10,7 +10,7 @@ import ( ) func RenderToPng(c *middleware.Context) { - accountId := c.UsingAccountId + accountId := c.AccountId queryReader := util.NewUrlQueryReader(c.Req.URL) queryParams := "?render=1&accountId=" + strconv.FormatInt(accountId, 10) + "&" + c.Req.URL.RawQuery diff --git a/pkg/api/search.go b/pkg/api/search.go index 3bbd4dd7a39..66129ace325 100644 --- a/pkg/api/search.go +++ b/pkg/api/search.go @@ -31,7 +31,7 @@ func Search(c *middleware.Context) { query := m.SearchDashboardsQuery{ Title: matches[3], Tag: matches[2], - AccountId: c.UsingAccountId, + AccountId: c.AccountId, } err := bus.Dispatch(&query) if err != nil { diff --git a/pkg/api/signup.go b/pkg/api/signup.go index 95852e22bda..a678a7adec7 100644 --- a/pkg/api/signup.go +++ b/pkg/api/signup.go @@ -9,7 +9,7 @@ import ( // POST /api/account/signup func SignUp(c *middleware.Context) { - var cmd m.CreateAccountCommand + var cmd m.CreateUserCommand if !c.JsonBody(&cmd) { c.JsonApiErr(400, "Validation error", nil) @@ -21,9 +21,9 @@ func SignUp(c *middleware.Context) { cmd.Password = util.EncodePassword(cmd.Password, cmd.Salt) if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "failed to create account", err) + c.JsonApiErr(500, "failed to create user", err) return } - c.JsonOK("Account created") + c.JsonOK("User created") } diff --git a/pkg/api/user.go b/pkg/api/user.go new file mode 100644 index 00000000000..ad05ba27209 --- /dev/null +++ b/pkg/api/user.go @@ -0,0 +1,86 @@ +package api + +import ( + "github.com/torkelo/grafana-pro/pkg/bus" + "github.com/torkelo/grafana-pro/pkg/middleware" + m "github.com/torkelo/grafana-pro/pkg/models" +) + +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) + return + } + + c.JSON(200, query.Result) +} + +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) + return + } + + c.JsonOK("Account updated") +} + +func GetUserAccounts(c *middleware.Context) { + query := m.GetUserAccountsQuery{UserId: c.UserId} + + if err := bus.Dispatch(&query); err != nil { + c.JsonApiErr(500, "Failed to get user accounts", err) + return + } + + for _, ac := range query.Result { + if ac.AccountId == c.AccountId { + ac.IsUsing = true + break + } + } + + c.JSON(200, query.Result) +} + +func validateUsingAccount(userId int64, accountId int64) bool { + query := m.GetUserAccountsQuery{UserId: userId} + + if err := bus.Dispatch(&query); err != nil { + return false + } + + // validate that the account id in the list + valid := false + for _, other := range query.Result { + if other.AccountId == accountId { + valid = true + } + } + + return valid +} + +func SetUsingAccount(c *middleware.Context) { + usingAccountId := c.ParamsInt64(":id") + + if !validateUsingAccount(c.AccountId, usingAccountId) { + c.JsonApiErr(401, "Not a valid account", nil) + return + } + + cmd := m.SetUsingAccountCommand{ + UserId: c.UserId, + AccountId: usingAccountId, + } + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed change active account", err) + return + } + + c.JsonOK("Active account changed") +} diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index ca551abf94f..af1d9ba4792 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -14,18 +14,18 @@ type AuthOptions struct { ReqSignedIn bool } -func getRequestAccountId(c *Context) int64 { - accountId := c.Session.Get("accountId") +func getRequestUserId(c *Context) int64 { + userId := c.Session.Get("userId") - if accountId != nil { - return accountId.(int64) + if userId != nil { + return userId.(int64) } // TODO: figure out a way to secure this if c.Query("render") == "1" { - accountId := c.QueryInt64("accountId") - c.Session.Set("accountId", accountId) - return accountId + userId := c.QueryInt64("userId") + c.Session.Set("userId", userId) + return userId } return 0 @@ -54,7 +54,7 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler { return func(c *Context) { ok := false for _, role := range roles { - if role == c.UserRole { + if role == c.AccountRole { ok = true break } diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 9fb3f984b7c..f50ef3fc18a 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -31,10 +31,10 @@ func GetContextHandler() macaron.Handler { } // try get account id from request - if accountId := getRequestAccountId(ctx); accountId != 0 { - query := m.GetSignedInUserQuery{AccountId: accountId} + if userId := getRequestUserId(ctx); userId != 0 { + query := m.GetSignedInUserQuery{UserId: userId} if err := bus.Dispatch(&query); err != nil { - log.Error(3, "Failed to get user by id, %v, %v", accountId, err) + log.Error(3, "Failed to get user by id, %v, %v", userId, err) } else { ctx.IsSignedIn = true ctx.SignedInUser = query.Result @@ -47,20 +47,14 @@ func GetContextHandler() macaron.Handler { return } else { tokenInfo := tokenQuery.Result - query := m.GetSignedInUserQuery{AccountId: tokenInfo.AccountId} - if err := bus.Dispatch(&query); err != nil { - ctx.JsonApiErr(401, "Invalid token", err) - return - } ctx.IsSignedIn = true - ctx.SignedInUser = query.Result + ctx.SignedInUser = &m.SignedInUser{} - // api key role - ctx.UserRole = tokenInfo.Role + // TODO: fix this + ctx.AccountRole = tokenInfo.Role ctx.ApiKeyId = tokenInfo.Id - ctx.UsingAccountId = ctx.AccountId - ctx.UsingAccountName = ctx.UserName + ctx.AccountId = tokenInfo.AccountId } } diff --git a/pkg/models/account.go b/pkg/models/account.go index 73be1a5b7ad..37e3829da6c 100644 --- a/pkg/models/account.go +++ b/pkg/models/account.go @@ -10,60 +10,30 @@ var ( ErrAccountNotFound = errors.New("Account not found") ) -// Directly mapped to db schema, Do not change field names lighly type Account struct { - Id int64 - Login string `xorm:"UNIQUE NOT NULL"` - Email string `xorm:"UNIQUE NOT NULL"` - Name string - Password string - IsAdmin bool - Salt string `xorm:"VARCHAR(10)"` - Company string - UsingAccountId int64 - Created time.Time - Updated time.Time + Id int64 + Name string + Created time.Time + Updated time.Time } // --------------------- // 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:"-"` - IsAdmin bool `json:"-"` + Name string `json:"name"` Result Account `json:"-"` } type UpdateAccountCommand struct { - Email string `json:"email" binding:"Required"` - Login string `json:"login"` - Name string `json:"name"` - - AccountId int64 `json:"-"` + Name string `json:"name"` + AccountId int64 `json:"-"` } -type SetUsingAccountCommand struct { - AccountId int64 - UsingAccountId int64 -} - -// ---------------------- -// QUERIES - -type GetAccountInfoQuery struct { - Id int64 - Result AccountDTO -} - -type GetOtherAccountsQuery struct { - AccountId int64 - Result []*OtherAccountDTO +type GetUserAccountsQuery struct { + UserId int64 + Result []*UserAccountDTO } type GetAccountByIdQuery struct { @@ -71,55 +41,9 @@ type GetAccountByIdQuery struct { Result *Account } -type GetAccountByLoginQuery struct { - LoginOrEmail string - Result *Account -} - -type SearchAccountsQuery struct { - Query string - Page int - Limit int - - Result []*AccountSearchHitDTO -} - -type GetSignedInUserQuery struct { - AccountId int64 - Result *SignedInUser -} - -// ------------------------ -// DTO & Projections -type SignedInUser struct { - AccountId int64 - UsingAccountId int64 - UsingAccountName string - UserRole RoleType - UserLogin string - UserName string - UserEmail string - ApiKeyId int64 - IsGrafanaAdmin bool -} - -type OtherAccountDTO struct { +type UserAccountDTO struct { AccountId int64 `json:"accountId"` - Email string `json:"email"` + Name string `json:"email"` Role RoleType `json:"role"` IsUsing bool `json:"isUsing"` } - -type AccountSearchHitDTO struct { - Id int64 `json:"id"` - Name string `json:"name"` - Login string `json:"login"` - Email string `json:"email"` - IsAdmin bool `json:"isAdmin"` -} - -type AccountDTO struct { - Email string `json:"email"` - Name string `json:"name"` - Login string `json:"login"` -} diff --git a/pkg/models/account_user.go b/pkg/models/account_user.go new file mode 100644 index 00000000000..c1ca89590e7 --- /dev/null +++ b/pkg/models/account_user.go @@ -0,0 +1,66 @@ +package models + +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrInvalidRoleType = errors.New("Invalid role type") +) + +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:"account_id"` + UserId int64 `json:"user_id"` + Email string `json:"email"` + Login string `json:"login"` + Role string `json:"role"` +} diff --git a/pkg/models/collaborator.go b/pkg/models/collaborator.go deleted file mode 100644 index 371e07371e2..00000000000 --- a/pkg/models/collaborator.go +++ /dev/null @@ -1,67 +0,0 @@ -package models - -import ( - "errors" - "time" -) - -// Typed errors -var ( - ErrInvalidRoleType = errors.New("Invalid role type") -) - -type RoleType string - -const ( - ROLE_OWNER RoleType = "Owner" - 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 Collaborator struct { - Id int64 - AccountId int64 `xorm:"not null unique(uix_account_id_for_account_id)"` - Role RoleType `xorm:"not null"` - CollaboratorId int64 `xorm:"not null unique(uix_account_id_for_account_id)"` - - Created time.Time - Updated time.Time -} - -// --------------------- -// COMMANDS - -type RemoveCollaboratorCommand struct { - CollaboratorId int64 - AccountId int64 -} - -type AddCollaboratorCommand struct { - LoginOrEmail string `json:"loginOrEmail" binding:"Required"` - Role RoleType `json:"role" binding:"Required"` - AccountId int64 `json:"-"` - CollaboratorId int64 `json:"-"` -} - -// ---------------------- -// QUERIES - -type GetCollaboratorsQuery struct { - AccountId int64 - Result []*CollaboratorDTO -} - -// ---------------------- -// Projections and DTOs - -type CollaboratorDTO struct { - CollaboratorId int64 `json:"id"` - Email string `json:"email"` - Login string `json:"login"` - Role string `json:"role"` -} diff --git a/pkg/models/user.go b/pkg/models/user.go index d4da67d53b5..50533584c9c 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -1,6 +1,14 @@ package models -import "time" +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrUserNotFound = errors.New("User not found") +) type User struct { Id int64 @@ -9,6 +17,7 @@ type User struct { Login string Password string Salt string + Company string IsAdmin bool AccountId int64 @@ -17,30 +26,85 @@ type User struct { Updated time.Time } -type Account2 struct { - Id int64 - Name string - Created time.Time - Updated time.Time -} - -type AccountUser struct { - AccountId int64 - UserId int64 - Role RoleType - Created time.Time - Updated time.Time -} - // --------------------- // COMMANDS type CreateUserCommand struct { Email string Login string + Name string + Company string Password string Salt string IsAdmin bool Result User `json:"-"` } + +type UpdateUserCommand struct { + Name string `json:"name"` + Email string `json:"email"` + Login string `json:"login"` + + UserId int64 `json:"-"` +} + +type SetUsingAccountCommand struct { + UserId int64 + AccountId int64 +} + +// ---------------------- +// QUERIES + +type GetUserByLoginQuery struct { + LoginOrEmail string + Result *User +} + +type GetSignedInUserQuery struct { + UserId int64 + Result *SignedInUser +} + +type GetUserInfoQuery struct { + UserId int64 + Result UserDTO +} + +type SearchUsersQuery struct { + Query string + Page int + Limit int + + Result []*UserSearchHitDTO +} + +// ------------------------ +// DTO & Projections + +type SignedInUser struct { + UserId int64 + AccountId int64 + AccountName string + AccountRole RoleType + Login string + Name string + Email string + ApiKeyId int64 + IsGrafanaAdmin bool +} + +type UserDTO struct { + Email string `json:"email"` + Name string `json:"name"` + Login string `json:"login"` +} + +type UserSearchHitDTO struct { + Id int64 `json:"id"` + Name string `json:"name"` + Login string `json:"login"` + Email string `json:"email"` + IsAdmin bool `json:"isAdmin"` +} diff --git a/pkg/services/sqlstore/account_users.go b/pkg/services/sqlstore/account_users.go new file mode 100644 index 00000000000..b3a31f4357c --- /dev/null +++ b/pkg/services/sqlstore/account_users.go @@ -0,0 +1,51 @@ +package sqlstore + +import ( + "time" + + "github.com/go-xorm/xorm" + + "github.com/torkelo/grafana-pro/pkg/bus" + m "github.com/torkelo/grafana-pro/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", "account_user.user_id=user.id") + sess.Where("account_user.account_id=?", query.AccountId) + sess.Cols("account_user.account_id", "account_user.user_id", "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) + return err + }) +} diff --git a/pkg/services/sqlstore/accounts.go b/pkg/services/sqlstore/accounts.go index 3a1f2ca7982..201cdb15c6c 100644 --- a/pkg/services/sqlstore/accounts.go +++ b/pkg/services/sqlstore/accounts.go @@ -1,7 +1,6 @@ package sqlstore import ( - "strings" "time" "github.com/go-xorm/xorm" @@ -11,33 +10,20 @@ import ( ) func init() { - bus.AddHandler("sql", GetAccountInfo) - bus.AddHandler("sql", GetOtherAccounts) bus.AddHandler("sql", CreateAccount) bus.AddHandler("sql", SetUsingAccount) - bus.AddHandler("sql", GetAccountById) - bus.AddHandler("sql", GetAccountByLogin) - bus.AddHandler("sql", SearchAccounts) bus.AddHandler("sql", UpdateAccount) - bus.AddHandler("sql", GetSignedInUser) } func CreateAccount(cmd *m.CreateAccountCommand) error { return inTransaction(func(sess *xorm.Session) error { account := m.Account{ - Email: cmd.Email, - Name: cmd.Name, - Login: cmd.Login, - Password: cmd.Password, - Salt: cmd.Salt, - IsAdmin: cmd.IsAdmin, - Created: time.Now(), - Updated: time.Now(), + Name: cmd.Name, + Created: time.Now(), + Updated: time.Now(), } - sess.UseBool("is_admin") - _, err := sess.Insert(&account) cmd.Result = account return err @@ -48,8 +34,6 @@ func UpdateAccount(cmd *m.UpdateAccountCommand) error { return inTransaction(func(sess *xorm.Session) error { account := m.Account{ - Email: cmd.Email, - Login: cmd.Login, Name: cmd.Name, Updated: time.Now(), } @@ -58,137 +42,3 @@ func UpdateAccount(cmd *m.UpdateAccountCommand) error { return err }) } - -func SetUsingAccount(cmd *m.SetUsingAccountCommand) error { - return inTransaction(func(sess *xorm.Session) error { - account := m.Account{} - sess.Id(cmd.AccountId).Get(&account) - - account.UsingAccountId = cmd.UsingAccountId - _, err := sess.Id(account.Id).Update(&account) - return err - }) -} - -func GetAccountInfo(query *m.GetAccountInfoQuery) error { - var account m.Account - has, err := x.Id(query.Id).Get(&account) - - if err != nil { - return err - } else if has == false { - return m.ErrAccountNotFound - } - - query.Result = m.AccountDTO{ - Name: account.Name, - Email: account.Email, - Login: account.Login, - } - - return err -} - -func GetAccountById(query *m.GetAccountByIdQuery) error { - var err error - - var account m.Account - has, err := x.Id(query.Id).Get(&account) - - if err != nil { - return err - } else if has == false { - return m.ErrAccountNotFound - } - - if account.UsingAccountId == 0 { - account.UsingAccountId = account.Id - } - - query.Result = &account - - return nil -} - -func GetAccountByLogin(query *m.GetAccountByLoginQuery) error { - if query.LoginOrEmail == "" { - return m.ErrAccountNotFound - } - - account := new(m.Account) - if strings.Contains(query.LoginOrEmail, "@") { - account = &m.Account{Email: query.LoginOrEmail} - } else { - account = &m.Account{Login: strings.ToLower(query.LoginOrEmail)} - } - - has, err := x.Get(account) - - if err != nil { - return err - } else if has == false { - return m.ErrAccountNotFound - } - - if account.UsingAccountId == 0 { - account.UsingAccountId = account.Id - } - - query.Result = account - - return nil -} - -func GetOtherAccounts(query *m.GetOtherAccountsQuery) error { - query.Result = make([]*m.OtherAccountDTO, 0) - sess := x.Table("collaborator") - sess.Join("INNER", "account", "collaborator.account_id=account.id") - sess.Where("collaborator_id=?", query.AccountId) - sess.Cols("collaborator.account_id", "collaborator.role", "account.email") - err := sess.Find(&query.Result) - return err -} - -func SearchAccounts(query *m.SearchAccountsQuery) error { - query.Result = make([]*m.AccountSearchHitDTO, 0) - sess := x.Table("account") - sess.Where("email LIKE ?", query.Query+"%") - sess.Limit(query.Limit, query.Limit*query.Page) - sess.Cols("id", "email", "name", "login", "is_admin") - err := sess.Find(&query.Result) - return err -} - -func GetSignedInUser(query *m.GetSignedInUserQuery) error { - var rawSql = `SELECT - userAccount.id as account_id, - userAccount.is_admin as is_grafana_admin, - userAccount.email as user_email, - userAccount.name as user_name, - userAccount.login as user_login, - usingAccount.id as using_account_id, - usingAccount.name as using_account_name, - collaborator.role as user_role - FROM account as userAccount - LEFT OUTER JOIN account as usingAccount on usingAccount.id = userAccount.using_account_id - LEFT OUTER JOIN collaborator on collaborator.account_id = usingAccount.id AND collaborator.collaborator_id = userAccount.id - WHERE userAccount.id=?` - - var user m.SignedInUser - sess := x.Table("account") - has, err := sess.Sql(rawSql, query.AccountId).Get(&user) - if err != nil { - return err - } else if !has { - return m.ErrAccountNotFound - } - - if user.UsingAccountId == 0 || user.UsingAccountId == user.AccountId { - user.UsingAccountId = query.AccountId - user.UsingAccountName = user.UserName - user.UserRole = m.ROLE_OWNER - } - - query.Result = &user - return err -} diff --git a/pkg/services/sqlstore/accounts_test.go b/pkg/services/sqlstore/accounts_test.go index 856241bdbb8..50d6597d74f 100644 --- a/pkg/services/sqlstore/accounts_test.go +++ b/pkg/services/sqlstore/accounts_test.go @@ -13,87 +13,85 @@ func TestAccountDataAccess(t *testing.T) { Convey("Testing Account DB Access", t, func() { InitTestDB(t) - Convey("Given two saved accounts", func() { - ac1cmd := m.CreateAccountCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"} - ac2cmd := m.CreateAccountCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true} + Convey("Given two saved users", 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", IsAdmin: true} - err := CreateAccount(&ac1cmd) - err = CreateAccount(&ac2cmd) + err := CreateUser(&ac1cmd) + err = CreateUser(&ac2cmd) So(err, ShouldBeNil) ac1 := ac1cmd.Result ac2 := ac2cmd.Result - Convey("Should be able to read account info projection", func() { - query := m.GetAccountInfoQuery{Id: ac1.Id} - err = GetAccountInfo(&query) + Convey("Should be able to read user info projection", func() { + query := m.GetUserInfoQuery{UserId: ac1.Id} + err = GetUserInfo(&query) So(err, ShouldBeNil) So(query.Result.Email, ShouldEqual, "ac1@test.com") So(query.Result.Login, ShouldEqual, "ac1") }) - Convey("Can search accounts", func() { - query := m.SearchAccountsQuery{Query: ""} - err := SearchAccounts(&query) + Convey("Can search users", func() { + query := m.SearchUsersQuery{Query: ""} + err := SearchUsers(&query) So(err, ShouldBeNil) So(query.Result[0].Email, ShouldEqual, "ac1@test.com") So(query.Result[1].Email, ShouldEqual, "ac2@test.com") }) - Convey("Given an added collaborator", func() { - cmd := m.AddCollaboratorCommand{ - AccountId: ac1.Id, - CollaboratorId: ac2.Id, - Role: m.ROLE_VIEWER, + Convey("Given an added account user", func() { + cmd := m.AddAccountUserCommand{ + AccountId: ac1.AccountId, + UserId: ac2.Id, + Role: m.ROLE_VIEWER, } - err := AddCollaborator(&cmd) + err := AddAccountUser(&cmd) Convey("Should have been saved without error", func() { So(err, ShouldBeNil) }) Convey("Can get logged in user projection", func() { - query := m.GetSignedInUserQuery{AccountId: ac2.Id} + query := m.GetSignedInUserQuery{UserId: ac2.Id} err := GetSignedInUser(&query) So(err, ShouldBeNil) - So(query.Result.AccountId, ShouldEqual, ac2.Id) - So(query.Result.UserEmail, ShouldEqual, "ac2@test.com") - So(query.Result.UserName, ShouldEqual, "ac2 name") - So(query.Result.UserLogin, ShouldEqual, "ac2") - So(query.Result.UserRole, ShouldEqual, "Owner") - So(query.Result.UsingAccountName, ShouldEqual, "ac2 name") - So(query.Result.UsingAccountId, ShouldEqual, ac2.Id) + So(query.Result.AccountId, ShouldEqual, ac2.AccountId) + So(query.Result.Email, ShouldEqual, "ac2@test.com") + So(query.Result.Name, ShouldEqual, "ac2 name") + So(query.Result.Login, ShouldEqual, "ac2") + So(query.Result.AccountRole, ShouldEqual, "Admin") + So(query.Result.AccountName, ShouldEqual, "ac2@test.com") So(query.Result.IsGrafanaAdmin, ShouldBeTrue) }) - Convey("Can get other accounts", func() { - query := m.GetOtherAccountsQuery{AccountId: ac2.Id} - err := GetOtherAccounts(&query) + Convey("Can get user accounts", func() { + query := m.GetUserAccountsQuery{UserId: ac2.Id} + err := GetUserAccounts(&query) So(err, ShouldBeNil) - So(query.Result[0].Email, ShouldEqual, "ac1@test.com") + So(len(query.Result), ShouldEqual, 2) }) Convey("Can set using account", func() { - cmd := m.SetUsingAccountCommand{AccountId: ac2.Id, UsingAccountId: ac1.Id} + cmd := m.SetUsingAccountCommand{UserId: ac2.Id, AccountId: ac1.Id} err := SetUsingAccount(&cmd) So(err, ShouldBeNil) Convey("Logged in user query should return correct using account info", func() { - query := m.GetSignedInUserQuery{AccountId: ac2.Id} + query := m.GetSignedInUserQuery{UserId: ac2.Id} err := GetSignedInUser(&query) So(err, ShouldBeNil) - So(query.Result.AccountId, ShouldEqual, ac2.Id) - So(query.Result.UserEmail, ShouldEqual, "ac2@test.com") - So(query.Result.UserName, ShouldEqual, "ac2 name") - So(query.Result.UserLogin, ShouldEqual, "ac2") - So(query.Result.UsingAccountName, ShouldEqual, "ac1 name") - So(query.Result.UsingAccountId, ShouldEqual, ac1.Id) - So(query.Result.UserRole, ShouldEqual, "Viewer") + So(query.Result.AccountId, ShouldEqual, ac1.Id) + So(query.Result.Email, ShouldEqual, "ac2@test.com") + So(query.Result.Name, ShouldEqual, "ac2 name") + So(query.Result.Login, ShouldEqual, "ac2") + So(query.Result.AccountName, ShouldEqual, "ac1@test.com") + So(query.Result.AccountRole, ShouldEqual, "Viewer") }) }) }) diff --git a/pkg/services/sqlstore/collaborators.go b/pkg/services/sqlstore/collaborators.go deleted file mode 100644 index bb7fe897c32..00000000000 --- a/pkg/services/sqlstore/collaborators.go +++ /dev/null @@ -1,51 +0,0 @@ -package sqlstore - -import ( - "time" - - "github.com/go-xorm/xorm" - - "github.com/torkelo/grafana-pro/pkg/bus" - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func init() { - bus.AddHandler("sql", AddCollaborator) - bus.AddHandler("sql", RemoveCollaborator) - bus.AddHandler("sql", GetCollaborators) -} - -func AddCollaborator(cmd *m.AddCollaboratorCommand) error { - return inTransaction(func(sess *xorm.Session) error { - - entity := m.Collaborator{ - AccountId: cmd.AccountId, - CollaboratorId: cmd.CollaboratorId, - Role: cmd.Role, - Created: time.Now(), - Updated: time.Now(), - } - - _, err := sess.Insert(&entity) - return err - }) -} - -func GetCollaborators(query *m.GetCollaboratorsQuery) error { - query.Result = make([]*m.CollaboratorDTO, 0) - sess := x.Table("collaborator") - sess.Join("INNER", "account", "collaborator.collaborator_id=account.id") - sess.Where("collaborator.account_id=?", query.AccountId) - sess.Cols("collaborator.collaborator_id", "collaborator.role", "account.email", "account.login") - - err := sess.Find(&query.Result) - return err -} - -func RemoveCollaborator(cmd *m.RemoveCollaboratorCommand) error { - return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM collaborator WHERE collaborator_id=? and account_id=?" - _, err := sess.Exec(rawSql, cmd.CollaboratorId, cmd.AccountId) - return err - }) -} diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index 63f0ed8ceb5..6b54375fbaa 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -30,10 +30,15 @@ func AddMigrations(mg *Migrator) { &Column{Name: "created", Type: DB_DateTime, Nullable: false}, &Column{Name: "updated", Type: DB_DateTime, Nullable: false}, )) + //------- user table indexes ------------------ + mg.AddMigration("add unique index UIX_user.login", new(AddIndexMigration). + Name("UIX_user_login").Table("user").Columns("login")) + mg.AddMigration("add unique index UIX_user.email", new(AddIndexMigration). + Name("UIX_user_email").Table("user").Columns("email")) - //------- account2 table ------------------- - mg.AddMigration("create account2 table", new(AddTableMigration). - Name("account2").WithColumns( + //------- account table ------------------- + mg.AddMigration("create account table", new(AddTableMigration). + Name("account").WithColumns( &Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, &Column{Name: "name", Type: DB_NVarchar, Length: 255}, &Column{Name: "created", Type: DB_DateTime, Nullable: false}, @@ -41,7 +46,7 @@ func AddMigrations(mg *Migrator) { )) mg.AddMigration("add unique index UIX_account.name", new(AddIndexMigration). - Name("UIX_account_name").Table("account2").Columns("name")) + Name("UIX_account_name").Table("account").Columns("name")) //------- account_user table ------------------- mg.AddMigration("create account_user table", new(AddTableMigration). @@ -57,27 +62,6 @@ func AddMigrations(mg *Migrator) { mg.AddMigration("add unique index UIX_account_user", new(AddIndexMigration). Name("UIX_account_user").Table("account_user").Columns("account_id", "user_id")) - //------- account table ------------------- - mg.AddMigration("create account table", new(AddTableMigration). - Name("account").WithColumns( - &Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, - &Column{Name: "login", Type: DB_NVarchar, Length: 255}, - &Column{Name: "email", Type: DB_NVarchar, Length: 255}, - &Column{Name: "name", Type: DB_NVarchar, Length: 255}, - &Column{Name: "password", Type: DB_NVarchar, Length: 50}, - &Column{Name: "salt", Type: DB_NVarchar, Length: 50}, - &Column{Name: "company", Type: DB_NVarchar, Length: 255}, - &Column{Name: "using_account_id", Type: DB_BigInt}, - &Column{Name: "is_admin", Type: DB_Bool}, - &Column{Name: "created", Type: DB_DateTime}, - &Column{Name: "updated", Type: DB_DateTime}, - )) - - //------- account table indexes ------------------ - mg.AddMigration("add unique index UIX_account.login", new(AddIndexMigration). - Name("UIX_account_login").Table("account").Columns("login")) - mg.AddMigration("add unique index UIX_account.email", new(AddIndexMigration). - Name("UIX_account_email").Table("account").Columns("email")) } type MigrationLog struct { diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 1fa962226e6..84afd356f74 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -32,24 +32,18 @@ var ( UseSQLite3 bool ) -type TestTable struct { - Id1 int `xorm:"pk"` - Id2 int `xorm:"pk"` -} - func init() { tables = make([]interface{}, 0) - tables = append(tables, new(m.Dashboard), - new(m.Collaborator), new(m.DataSource), new(DashboardTag), - new(m.Token), new(TestTable)) + tables = append(tables, new(m.Dashboard), new(m.DataSource), new(DashboardTag), + new(m.Token)) } func EnsureAdminUser() { - adminQuery := m.GetAccountByLoginQuery{LoginOrEmail: setting.AdminUser} + adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser} - if err := bus.Dispatch(&adminQuery); err == m.ErrAccountNotFound { - cmd := m.CreateAccountCommand{} + if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound { + cmd := m.CreateUserCommand{} cmd.Login = setting.AdminUser cmd.Email = setting.AdminUser + "@localhost" cmd.Salt = util.GetRandomString(10) diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 51b5ed1a8f3..283d2fe2181 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -1,6 +1,7 @@ package sqlstore import ( + "strings" "time" "github.com/go-xorm/xorm" @@ -11,13 +12,19 @@ import ( func init() { bus.AddHandler("sql", CreateUser) + bus.AddHandler("sql", GetUserByLogin) + bus.AddHandler("sql", SetUsingAccount) + bus.AddHandler("sql", GetUserInfo) + bus.AddHandler("sql", GetSignedInUser) + bus.AddHandler("sql", SearchUsers) + bus.AddHandler("sql", GetUserAccounts) } func CreateUser(cmd *m.CreateUserCommand) error { return inTransaction(func(sess *xorm.Session) error { // create account - account := m.Account2{ + account := m.Account{ Name: cmd.Email, Created: time.Now(), Updated: time.Now(), @@ -31,6 +38,9 @@ func CreateUser(cmd *m.CreateUserCommand) error { user := m.User{ Email: cmd.Email, Password: cmd.Password, + Name: cmd.Name, + Login: cmd.Login, + Company: cmd.Company, Salt: cmd.Salt, IsAdmin: cmd.IsAdmin, AccountId: account.Id, @@ -56,3 +66,106 @@ func CreateUser(cmd *m.CreateUserCommand) error { return err }) } + +func GetUserByLogin(query *m.GetUserByLoginQuery) error { + if query.LoginOrEmail == "" { + return m.ErrAccountNotFound + } + + user := new(m.User) + if strings.Contains(query.LoginOrEmail, "@") { + user = &m.User{Email: query.LoginOrEmail} + } else { + user = &m.User{Login: strings.ToLower(query.LoginOrEmail)} + } + + has, err := x.Get(user) + + if err != nil { + return err + } else if has == false { + return m.ErrUserNotFound + } + + query.Result = user + + return nil +} + +func SetUsingAccount(cmd *m.SetUsingAccountCommand) error { + return inTransaction(func(sess *xorm.Session) error { + user := m.User{} + sess.Id(cmd.UserId).Get(&user) + + user.AccountId = cmd.AccountId + _, err := sess.Id(user.Id).Update(&user) + return err + }) +} + +func GetUserInfo(query *m.GetUserInfoQuery) error { + var user m.User + has, err := x.Id(query.UserId).Get(&user) + + if err != nil { + return err + } else if has == false { + return m.ErrUserNotFound + } + + query.Result = m.UserDTO{ + Name: user.Name, + Email: user.Email, + Login: user.Login, + } + + 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") + err := sess.Find(&query.Result) + return err +} + +func GetSignedInUser(query *m.GetSignedInUserQuery) error { + var rawSql = `SELECT + user.id as user_id, + user.is_admin as is_grafana_admin, + user.email as email, + user.login as login, + user.name as name, + account.name as account_name, + account_user.role as account_role, + account.id as account_id + FROM user + LEFT OUTER JOIN account_user on account_user.account_id = user.account_id and account_user.user_id = user.id + LEFT OUTER JOIN account on account.id = user.account_id + WHERE user.id=?` + + var user m.SignedInUser + sess := x.Table("user") + has, err := sess.Sql(rawSql, query.UserId).Get(&user) + if err != nil { + return err + } else if !has { + return m.ErrUserNotFound + } + + query.Result = &user + return err +} + +func SearchUsers(query *m.SearchUsersQuery) error { + query.Result = make([]*m.UserSearchHitDTO, 0) + sess := x.Table("user") + sess.Where("email LIKE ?", query.Query+"%") + sess.Limit(query.Limit, query.Limit*query.Page) + sess.Cols("id", "email", "name", "login", "is_admin") + err := sess.Find(&query.Result) + return err +} diff --git a/pkg/services/sqlstore/user_test.go b/pkg/services/sqlstore/user_test.go deleted file mode 100644 index 5af6575c4d8..00000000000 --- a/pkg/services/sqlstore/user_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package sqlstore - -import ( - "fmt" - "testing" - - . "github.com/smartystreets/goconvey/convey" - - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func TestUserDataAccess(t *testing.T) { - - Convey("Testing User DB", t, func() { - InitTestDB(t) - - Convey("When creating a user", func() { - ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com"} - - err := CreateUser(&ac1cmd) - So(err, ShouldBeNil) - - ac1 := ac1cmd.Result - fmt.Printf("%v", ac1) - - Convey("Should be able to read account info projection", func() { - }) - }) - }) -}