diff --git a/pkg/api/api.go b/pkg/api/api.go index b61517c9d56..c6d050547ef 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -50,6 +50,7 @@ func Register(r *macaron.Macaron) { r.Get("/accounts", GetUserAccounts) r.Post("/stars/dashboard/:id", StarDashboard) r.Delete("/stars/dashboard/:id", UnstarDashboard) + r.Put("/password", bind(m.ChangeUserPasswordCommand{}), ChangeUserPassword) }) // account diff --git a/pkg/api/user.go b/pkg/api/user.go index 306f7c7b3d4..c7f8214679b 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -4,6 +4,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/util" ) func GetUser(c *middleware.Context) { @@ -84,3 +85,33 @@ func SetUsingAccount(c *middleware.Context) { c.JsonOK("Active account changed") } + +func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) { + userQuery := m.GetUserByIdQuery{Id: c.UserId} + + if err := bus.Dispatch(&userQuery); err != nil { + c.JsonApiErr(500, "Could not read user from database", err) + return + } + + passwordHashed := util.EncodePassword(cmd.OldPassword, userQuery.Result.Salt) + if passwordHashed != userQuery.Result.Password { + c.JsonApiErr(401, "Invalid old password", nil) + return + } + + if len(cmd.NewPassword) < 4 { + c.JsonApiErr(400, "New password too short", nil) + return + } + + cmd.UserId = c.UserId + cmd.NewPassword = util.EncodePassword(cmd.NewPassword, userQuery.Result.Salt) + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to change user password", err) + return + } + + c.JsonOK("User password changed") +} diff --git a/pkg/models/user.go b/pkg/models/user.go index 81ed0123c1e..2905dce8ba9 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -52,6 +52,13 @@ type UpdateUserCommand struct { UserId int64 `json:"-"` } +type ChangeUserPasswordCommand struct { + OldPassword string `json:"oldPassword"` + NewPassword string `json:"newPassword"` + + UserId int64 `json:"-"` +} + type DeleteUserCommand struct { UserId int64 } diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index bfa5ebc639a..f8773eb26cf 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -18,6 +18,7 @@ func init() { bus.AddHandler("sql", CreateUser) bus.AddHandler("sql", GetUserById) bus.AddHandler("sql", UpdateUser) + bus.AddHandler("sql", ChangeUserPassword) bus.AddHandler("sql", GetUserByLogin) bus.AddHandler("sql", SetUsingAccount) bus.AddHandler("sql", GetUserInfo) @@ -181,6 +182,22 @@ func UpdateUser(cmd *m.UpdateUserCommand) error { }) } +func ChangeUserPassword(cmd *m.ChangeUserPasswordCommand) error { + return inTransaction2(func(sess *session) error { + + user := m.User{ + Password: cmd.NewPassword, + Updated: time.Now(), + } + + if _, err := sess.Id(cmd.UserId).Update(&user); err != nil { + return err + } + + return nil + }) +} + func SetUsingAccount(cmd *m.SetUsingAccountCommand) error { return inTransaction(func(sess *xorm.Session) error { user := m.User{} diff --git a/src/app/features/all.js b/src/app/features/all.js index 635b4b5b527..004d3eebe9f 100644 --- a/src/app/features/all.js +++ b/src/app/features/all.js @@ -9,6 +9,7 @@ define([ './dashboard/all', './panel/all', './profile/profileCtrl', + './profile/changePasswordCtrl', './account/all', './admin/all', './grafanaDatasource/datasource', diff --git a/src/app/features/opentsdb/datasource.js b/src/app/features/opentsdb/datasource.js index d5bdd011dcf..16cb58f94cb 100644 --- a/src/app/features/opentsdb/datasource.js +++ b/src/app/features/opentsdb/datasource.js @@ -120,7 +120,7 @@ function (angular, _, kbn) { } function convertTargetToQuery(target) { - if (!target.metric) { + if (!target.metric || target.hide) { return null; } diff --git a/src/app/features/profile/changePasswordCtrl.js b/src/app/features/profile/changePasswordCtrl.js new file mode 100644 index 00000000000..b2bb90ea6cf --- /dev/null +++ b/src/app/features/profile/changePasswordCtrl.js @@ -0,0 +1,28 @@ +define([ + 'angular', + 'config', +], +function (angular) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('ChangePasswordCtrl', function($scope, backendSrv, $location) { + + $scope.command = {}; + + $scope.changePassword = function() { + if (!$scope.userForm.$valid) { return; } + + if ($scope.command.newPassword !== $scope.command.confirmNew) { + $scope.appEvent('alert-warning', ['New passwords do not match', '']); + return; + } + + backendSrv.put('/api/user/password', $scope.command).then(function() { + $location.path("profile"); + }); + }; + + }); +}); diff --git a/src/app/features/profile/partials/password.html b/src/app/features/profile/partials/password.html new file mode 100644 index 00000000000..f7b5a18e5d0 --- /dev/null +++ b/src/app/features/profile/partials/password.html @@ -0,0 +1,56 @@ + + + + +
+
+ +

Change password

+ +
+
+
+
    +
  • + Old Password +
  • +
  • + +
  • +
+
+
+
+
    +
  • + New Password +
  • +
  • + +
  • +
+
+
+
+
    +
  • + Confirm New +
  • +
  • + +
  • +
+
+
+
+ +
+ +
+ +
+
+ diff --git a/src/app/features/profile/partials/profile.html b/src/app/features/profile/partials/profile.html index db9d9b52702..ac9b5298774 100644 --- a/src/app/features/profile/partials/profile.html +++ b/src/app/features/profile/partials/profile.html @@ -1,6 +1,8 @@ - + +
diff --git a/src/app/features/profile/profileCtrl.js b/src/app/features/profile/profileCtrl.js index fab5367c726..dd957fb1228 100644 --- a/src/app/features/profile/profileCtrl.js +++ b/src/app/features/profile/profileCtrl.js @@ -7,7 +7,7 @@ function (angular, config) { var module = angular.module('grafana.controllers'); - module.controller('ProfileCtrl', function($scope, $http, backendSrv) { + module.controller('ProfileCtrl', function($scope, backendSrv) { $scope.newAccount = {name: ''}; diff --git a/src/app/routes/backend/all.js b/src/app/routes/backend/all.js index b211550d841..510f4ffb16c 100644 --- a/src/app/routes/backend/all.js +++ b/src/app/routes/backend/all.js @@ -62,6 +62,10 @@ define([ templateUrl: 'app/features/profile/partials/profile.html', controller : 'ProfileCtrl', }) + .when('/profile/password', { + templateUrl: 'app/features/profile/partials/password.html', + controller : 'ChangePasswordCtrl', + }) .when('/admin/settings', { templateUrl: 'app/features/admin/partials/settings.html', controller : 'AdminSettingsCtrl',