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 @@
+