Chore: Port user services to identity.Requester (#73851)

* port api key api to signedinuser

* port users to signed in user interface

* fix tests
pull/73893/head
Jo 2 years ago committed by GitHub
parent 01d98114b9
commit 5eed495cce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      pkg/api/admin_users.go
  2. 24
      pkg/api/apikey.go
  3. 5
      pkg/api/common_test.go
  4. 5
      pkg/api/dtos/models.go
  5. 21
      pkg/api/org.go
  6. 4
      pkg/api/org_test.go
  7. 24
      pkg/api/org_users.go
  8. 137
      pkg/api/user.go
  9. 26
      pkg/api/user_token.go
  10. 4
      pkg/services/apikey/model.go
  11. 2
      pkg/services/auth/identity/requester.go

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/org"
@ -384,8 +385,15 @@ func (hs *HTTPServer) AdminLogoutUser(c *contextmodel.ReqContext) response.Respo
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
if c.UserID == userID {
return response.Error(400, "You cannot logout yourself", nil)
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace == identity.NamespaceUser {
activeUserID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to parse active user id", err)
}
if activeUserID == userID {
return response.Error(http.StatusBadRequest, "You cannot logout yourself", nil)
}
}
return hs.logoutUserFromAllDevicesInternal(c.Req.Context(), userID)

@ -32,11 +32,11 @@ import (
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) GetAPIKeys(c *contextmodel.ReqContext) response.Response {
query := apikey.GetApiKeysQuery{OrgID: c.OrgID, User: c.SignedInUser, IncludeExpired: c.QueryBool("includeExpired")}
query := apikey.GetApiKeysQuery{OrgID: c.SignedInUser.GetOrgID(), User: c.SignedInUser, IncludeExpired: c.QueryBool("includeExpired")}
keys, err := hs.apiKeyService.GetAPIKeys(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Failed to list api keys", err)
return response.Error(http.StatusInternalServerError, "Failed to list api keys", err)
}
ids := map[string]bool{}
@ -87,7 +87,7 @@ func (hs *HTTPServer) DeleteAPIKey(c *contextmodel.ReqContext) response.Response
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
cmd := &apikey.DeleteCommand{ID: id, OrgID: c.OrgID}
cmd := &apikey.DeleteCommand{ID: id, OrgID: c.SignedInUser.GetOrgID()}
err = hs.apiKeyService.DeleteApiKey(c.Req.Context(), cmd)
if err != nil {
var status int
@ -126,38 +126,38 @@ func (hs *HTTPServer) AddAPIKey(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !cmd.Role.IsValid() {
return response.Error(400, "Invalid role specified", nil)
return response.Error(http.StatusBadRequest, "Invalid role specified", nil)
}
if !c.OrgRole.Includes(cmd.Role) {
if !c.SignedInUser.GetOrgRole().Includes(cmd.Role) {
return response.Error(http.StatusForbidden, "Cannot assign a role higher than user's role", nil)
}
if hs.Cfg.ApiKeyMaxSecondsToLive != -1 {
if cmd.SecondsToLive == 0 {
return response.Error(400, "Number of seconds before expiration should be set", nil)
return response.Error(http.StatusBadRequest, "Number of seconds before expiration should be set", nil)
}
if cmd.SecondsToLive > hs.Cfg.ApiKeyMaxSecondsToLive {
return response.Error(400, "Number of seconds before expiration is greater than the global limit", nil)
return response.Error(http.StatusBadRequest, "Number of seconds before expiration is greater than the global limit", nil)
}
}
cmd.OrgID = c.OrgID
cmd.OrgID = c.SignedInUser.GetOrgID()
newKeyInfo, err := apikeygen.New(cmd.OrgID, cmd.Name)
if err != nil {
return response.Error(500, "Generating API key failed", err)
return response.Error(http.StatusInternalServerError, "Generating API key failed", err)
}
cmd.Key = newKeyInfo.HashedKey
key, err := hs.apiKeyService.AddAPIKey(c.Req.Context(), &cmd)
if err != nil {
if errors.Is(err, apikey.ErrInvalidExpiration) {
return response.Error(400, err.Error(), nil)
return response.Error(http.StatusBadRequest, err.Error(), nil)
}
if errors.Is(err, apikey.ErrDuplicate) {
return response.Error(409, err.Error(), nil)
return response.Error(http.StatusConflict, err.Error(), nil)
}
return response.Error(500, "Failed to add API Key", err)
return response.Error(http.StatusInternalServerError, "Failed to add API Key", err)
}
result := &dtos.NewApiKeyResult{

@ -214,6 +214,11 @@ func setupScenarioContext(t *testing.T, url string) *scenarioContext {
return sc
}
// FIXME: This user should not be anonymous
func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}
}
// FIXME: This user should not be anonymous
func userWithPermissions(orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
return &user.SignedInUser{IsAnonymous: true, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}

@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -142,8 +143,8 @@ func GetGravatarUrlWithDefault(text string, defaultText string) string {
return GetGravatarUrl(text)
}
func IsHiddenUser(userLogin string, signedInUser *user.SignedInUser, cfg *setting.Cfg) bool {
if userLogin == "" || signedInUser.IsGrafanaAdmin || userLogin == signedInUser.Login {
func IsHiddenUser(userLogin string, signedInUser identity.Requester, cfg *setting.Cfg) bool {
if userLogin == "" || signedInUser.GetIsGrafanaAdmin() || userLogin == signedInUser.GetLogin() {
return false
}

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
@ -25,7 +26,7 @@ import (
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) GetCurrentOrg(c *contextmodel.ReqContext) response.Response {
return hs.getOrgHelper(c.Req.Context(), c.OrgID)
return hs.getOrgHelper(c.Req.Context(), c.SignedInUser.GetOrgID())
}
// swagger:route GET /orgs/{org_id} orgs getOrgByID
@ -131,7 +132,17 @@ func (hs *HTTPServer) CreateOrg(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.UserID = c.UserID
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace != identity.NamespaceUser {
return response.Error(http.StatusForbidden, "Only users can create organizations", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
}
cmd.UserID = userID
result, err := hs.orgService.CreateWithMember(c.Req.Context(), &cmd)
if err != nil {
if errors.Is(err, org.ErrOrgNameTaken) {
@ -163,7 +174,7 @@ func (hs *HTTPServer) UpdateCurrentOrg(c *contextmodel.ReqContext) response.Resp
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.updateOrgHelper(c.Req.Context(), form, c.OrgID)
return hs.updateOrgHelper(c.Req.Context(), form, c.SignedInUser.GetOrgID())
}
// swagger:route PUT /orgs/{org_id} orgs updateOrg
@ -218,7 +229,7 @@ func (hs *HTTPServer) UpdateCurrentOrgAddress(c *contextmodel.ReqContext) respon
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.updateOrgAddressHelper(c.Req.Context(), form, c.OrgID)
return hs.updateOrgAddressHelper(c.Req.Context(), form, c.SignedInUser.GetOrgID())
}
// swagger:route PUT /orgs/{org_id}/address orgs updateOrgAddress
@ -283,7 +294,7 @@ func (hs *HTTPServer) DeleteOrgByID(c *contextmodel.ReqContext) response.Respons
return response.Error(http.StatusBadRequest, "orgId is invalid", err)
}
// before deleting an org, check if user does not belong to the current org
if c.OrgID == orgID {
if c.SignedInUser.GetOrgID() == orgID {
return response.Error(http.StatusBadRequest, "Can not delete org for current user", nil)
}

@ -174,11 +174,11 @@ func TestAPIEndpoint_CreateOrgs(t *testing.T) {
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
hs.accesscontrolService = actest.FakeService{}
hs.userService = &usertest.FakeUserService{
ExpectedSignedInUser: &user.SignedInUser{OrgID: 0},
ExpectedSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 0},
}
})
req := webtest.RequestWithSignedInUser(server.NewPostRequest("/api/orgs", strings.NewReader(`{"name": "test"}`)), userWithPermissions(0, tt.permission))
req := webtest.RequestWithSignedInUser(server.NewPostRequest("/api/orgs", strings.NewReader(`{"name": "test"}`)), authedUserWithPermissions(1, 0, tt.permission))
res, err := server.SendJSON(req)
require.NoError(t, err)
assert.Equal(t, tt.expectedCode, res.StatusCode)

@ -38,7 +38,7 @@ func (hs *HTTPServer) AddOrgUserToCurrentOrg(c *contextmodel.ReqContext) respons
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgID = c.OrgID
cmd.OrgID = c.SignedInUser.GetOrgID()
return hs.addOrgUserHelper(c, cmd)
}
@ -72,28 +72,28 @@ func (hs *HTTPServer) AddOrgUser(c *contextmodel.ReqContext) response.Response {
func (hs *HTTPServer) addOrgUserHelper(c *contextmodel.ReqContext, cmd org.AddOrgUserCommand) response.Response {
if !cmd.Role.IsValid() {
return response.Error(400, "Invalid role specified", nil)
return response.Error(http.StatusBadRequest, "Invalid role specified", nil)
}
if !c.OrgRole.Includes(cmd.Role) && !c.IsGrafanaAdmin {
if !c.SignedInUser.GetOrgRole().Includes(cmd.Role) && !c.SignedInUser.GetIsGrafanaAdmin() {
return response.Error(http.StatusForbidden, "Cannot assign a role higher than user's role", nil)
}
userQuery := user.GetUserByLoginQuery{LoginOrEmail: cmd.LoginOrEmail}
userToAdd, err := hs.userService.GetByLogin(c.Req.Context(), &userQuery)
if err != nil {
return response.Error(404, "User not found", nil)
return response.Error(http.StatusNotFound, "User not found", nil)
}
cmd.UserID = userToAdd.ID
if err := hs.orgService.AddOrgUser(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, org.ErrOrgUserAlreadyAdded) {
return response.JSON(409, util.DynMap{
return response.JSON(http.StatusConflict, util.DynMap{
"message": "User is already member of this organization",
"userId": cmd.UserID,
})
}
return response.Error(500, "Could not add user to organization", err)
return response.Error(http.StatusInternalServerError, "Could not add user to organization", err)
}
return response.JSON(http.StatusOK, util.DynMap{
@ -117,7 +117,7 @@ func (hs *HTTPServer) addOrgUserHelper(c *contextmodel.ReqContext, cmd org.AddOr
// 500: internalServerError
func (hs *HTTPServer) GetOrgUsersForCurrentOrg(c *contextmodel.ReqContext) response.Response {
result, err := hs.searchOrgUsersHelper(c, &org.SearchOrgUsersQuery{
OrgID: c.OrgID,
OrgID: c.SignedInUser.GetOrgID(),
Query: c.Query("query"),
Limit: c.QueryInt("limit"),
User: c.SignedInUser,
@ -146,7 +146,7 @@ func (hs *HTTPServer) GetOrgUsersForCurrentOrg(c *contextmodel.ReqContext) respo
func (hs *HTTPServer) GetOrgUsersForCurrentOrgLookup(c *contextmodel.ReqContext) response.Response {
orgUsersResult, err := hs.searchOrgUsersHelper(c, &org.SearchOrgUsersQuery{
OrgID: c.OrgID,
OrgID: c.SignedInUser.GetOrgID(),
Query: c.Query("query"),
Limit: c.QueryInt("limit"),
User: c.SignedInUser,
@ -265,7 +265,7 @@ func (hs *HTTPServer) SearchOrgUsersWithPaging(c *contextmodel.ReqContext) respo
}
query := &org.SearchOrgUsersQuery{
OrgID: c.OrgID,
OrgID: c.SignedInUser.GetOrgID(),
Query: c.Query("query"),
Page: page,
Limit: perPage,
@ -351,7 +351,7 @@ func (hs *HTTPServer) UpdateOrgUserForCurrentOrg(c *contextmodel.ReqContext) res
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgID = c.OrgID
cmd.OrgID = c.SignedInUser.GetOrgID()
var err error
cmd.UserID, err = strconv.ParseInt(web.Params(c.Req)[":userId"], 10, 64)
if err != nil {
@ -394,7 +394,7 @@ func (hs *HTTPServer) updateOrgUserHelper(c *contextmodel.ReqContext, cmd org.Up
if !cmd.Role.IsValid() {
return response.Error(http.StatusBadRequest, "Invalid role specified", nil)
}
if !c.OrgRole.Includes(cmd.Role) && !c.IsGrafanaAdmin {
if !c.SignedInUser.GetOrgRole().Includes(cmd.Role) && !c.SignedInUser.GetIsGrafanaAdmin() {
return response.Error(http.StatusForbidden, "Cannot assign a role higher than user's role", nil)
}
@ -453,7 +453,7 @@ func (hs *HTTPServer) RemoveOrgUserForCurrentOrg(c *contextmodel.ReqContext) res
return hs.removeOrgUserHelper(c.Req.Context(), &org.RemoveOrgUserCommand{
UserID: userId,
OrgID: c.OrgID,
OrgID: c.SignedInUser.GetOrgID(),
ShouldDeleteOrphanedUser: true,
})
}

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/org"
@ -29,7 +30,11 @@ import (
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) GetSignedInUser(c *contextmodel.ReqContext) response.Response {
return hs.getUserUserProfile(c, c.UserID)
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
return hs.getUserUserProfile(c, userID)
}
// swagger:route GET /users/{user_id} users getUserByID
@ -75,7 +80,7 @@ func (hs *HTTPServer) getUserUserProfile(c *contextmodel.ReqContext, userID int6
userProfile.IsGrafanaAdminExternallySynced = login.IsGrafanaAdminExternallySynced(hs.Cfg, authInfo.AuthModule, oAuthAndAllowAssignGrafanaAdmin)
}
userProfile.AccessControl = hs.getAccessControlMetadata(c, c.OrgID, "global.users:id:", strconv.FormatInt(userID, 10))
userProfile.AccessControl = hs.getAccessControlMetadata(c, c.SignedInUser.GetOrgID(), "global.users:id:", strconv.FormatInt(userID, 10))
userProfile.AvatarURL = dtos.GetGravatarUrl(userProfile.Email)
return response.JSON(http.StatusOK, userProfile)
@ -133,15 +138,21 @@ func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Re
cmd.Email = strings.TrimSpace(cmd.Email)
cmd.Login = strings.TrimSpace(cmd.Login)
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
if hs.Cfg.AuthProxyEnabled {
if hs.Cfg.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
return response.Error(400, "Not allowed to change email when auth proxy is using email property", nil)
if hs.Cfg.AuthProxyHeaderProperty == "email" && cmd.Email != c.SignedInUser.GetEmail() {
return response.Error(http.StatusBadRequest, "Not allowed to change email when auth proxy is using email property", nil)
}
if hs.Cfg.AuthProxyHeaderProperty == "username" && cmd.Login != c.Login {
return response.Error(400, "Not allowed to change username when auth proxy is using username property", nil)
if hs.Cfg.AuthProxyHeaderProperty == "username" && cmd.Login != c.SignedInUser.GetLogin() {
return response.Error(http.StatusBadRequest, "Not allowed to change username when auth proxy is using username property", nil)
}
}
cmd.UserID = c.UserID
cmd.UserID = userID
return hs.handleUpdateUser(c.Req.Context(), cmd)
}
@ -256,7 +267,12 @@ func (hs *HTTPServer) isExternalUser(ctx context.Context, userID int64) (bool, e
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) GetSignedInUserOrgList(c *contextmodel.ReqContext) response.Response {
return hs.getUserOrgList(c.Req.Context(), c.UserID)
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
return hs.getUserOrgList(c.Req.Context(), userID)
}
// swagger:route GET /user/teams signed_in_user getSignedInUserTeamList
@ -271,7 +287,12 @@ func (hs *HTTPServer) GetSignedInUserOrgList(c *contextmodel.ReqContext) respons
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) GetSignedInUserTeamList(c *contextmodel.ReqContext) response.Response {
return hs.getUserTeamList(c, c.OrgID, c.UserID)
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
return hs.getUserTeamList(c, c.SignedInUser.GetOrgID(), userID)
}
// swagger:route GET /users/{user_id}/teams users getUserTeams
@ -291,7 +312,7 @@ func (hs *HTTPServer) GetUserTeams(c *contextmodel.ReqContext) response.Response
if err != nil {
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
return hs.getUserTeamList(c, c.OrgID, id)
return hs.getUserTeamList(c, c.SignedInUser.GetOrgID(), id)
}
func (hs *HTTPServer) getUserTeamList(c *contextmodel.ReqContext, orgID int64, userID int64) response.Response {
@ -299,7 +320,7 @@ func (hs *HTTPServer) getUserTeamList(c *contextmodel.ReqContext, orgID int64, u
queryResult, err := hs.teamService.GetTeamsByUser(c.Req.Context(), &query)
if err != nil {
return response.Error(500, "Failed to get user teams", err)
return response.Error(http.StatusInternalServerError, "Failed to get user teams", err)
}
for _, team := range queryResult {
@ -333,7 +354,7 @@ func (hs *HTTPServer) getUserOrgList(ctx context.Context, userID int64) response
result, err := hs.orgService.GetUserOrgList(ctx, &query)
if err != nil {
return response.Error(500, "Failed to get user organizations", err)
return response.Error(http.StatusInternalServerError, "Failed to get user organizations", err)
}
return response.JSON(http.StatusOK, result)
@ -376,14 +397,19 @@ func (hs *HTTPServer) UserSetUsingOrg(c *contextmodel.ReqContext) response.Respo
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
if !hs.validateUsingOrg(c.Req.Context(), c.UserID, orgID) {
return response.Error(401, "Not a valid organization", nil)
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
if !hs.validateUsingOrg(c.Req.Context(), userID, orgID) {
return response.Error(http.StatusUnauthorized, "Not a valid organization", nil)
}
cmd := user.SetUsingOrgCommand{UserID: c.UserID, OrgID: orgID}
cmd := user.SetUsingOrgCommand{UserID: userID, OrgID: orgID}
if err := hs.userService.SetUsingOrg(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to change active organization", err)
return response.Error(http.StatusInternalServerError, "Failed to change active organization", err)
}
return response.Success("Active organization changed")
@ -397,14 +423,27 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *contextmodel.ReqContex
return
}
if !hs.validateUsingOrg(c.Req.Context(), c.UserID, orgID) {
hs.NotFoundHandler(c)
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace != identity.NamespaceUser {
c.JsonApiErr(http.StatusForbidden, "Endpoint only available for users", nil)
return
}
cmd := user.SetUsingOrgCommand{UserID: c.UserID, OrgID: orgID}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
c.JsonApiErr(http.StatusInternalServerError, "Failed to parse user id", err)
return
}
if !hs.validateUsingOrg(c.Req.Context(), userID, orgID) {
hs.NotFoundHandler(c)
return
}
cmd := user.SetUsingOrgCommand{UserID: userID, OrgID: orgID}
if err := hs.userService.SetUsingOrg(c.Req.Context(), &cmd); err != nil {
hs.NotFoundHandler(c)
return
}
c.Redirect(hs.Cfg.AppSubURL + "/")
@ -431,42 +470,47 @@ func (hs *HTTPServer) ChangeUserPassword(c *contextmodel.ReqContext) response.Re
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userQuery := user.GetUserByIDQuery{ID: c.UserID}
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
userQuery := user.GetUserByIDQuery{ID: userID}
usr, err := hs.userService.GetByID(c.Req.Context(), &userQuery)
if err != nil {
return response.Error(500, "Could not read user from database", err)
return response.Error(http.StatusInternalServerError, "Could not read user from database", err)
}
getAuthQuery := login.GetAuthInfoQuery{UserId: usr.ID}
if authInfo, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &getAuthQuery); err == nil {
authModule := authInfo.AuthModule
if authModule == login.LDAPAuthModule || authModule == login.AuthProxyAuthModule {
return response.Error(400, "Not allowed to reset password for LDAP or Auth Proxy user", nil)
return response.Error(http.StatusBadRequest, "Not allowed to reset password for LDAP or Auth Proxy user", nil)
}
}
passwordHashed, err := util.EncodePassword(cmd.OldPassword, usr.Salt)
if err != nil {
return response.Error(500, "Failed to encode password", err)
return response.Error(http.StatusInternalServerError, "Failed to encode password", err)
}
if passwordHashed != usr.Password {
return response.Error(401, "Invalid old password", nil)
return response.Error(http.StatusUnauthorized, "Invalid old password", nil)
}
password := user.Password(cmd.NewPassword)
if password.IsWeak() {
return response.Error(400, "New password is too short", nil)
return response.Error(http.StatusBadRequest, "New password is too short", nil)
}
cmd.UserID = c.UserID
cmd.UserID = userID
cmd.NewPassword, err = util.EncodePassword(cmd.NewPassword, usr.Salt)
if err != nil {
return response.Error(500, "Failed to encode password", err)
return response.Error(http.StatusInternalServerError, "Failed to encode password", err)
}
if err := hs.userService.ChangePassword(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to change user password", err)
return response.Error(http.StatusInternalServerError, "Failed to change user password", err)
}
return response.Success("User password changed")
@ -492,16 +536,26 @@ func (hs *HTTPServer) SetHelpFlag(c *contextmodel.ReqContext) response.Response
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
bitmask := &c.HelpFlags1
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
usr, err := hs.userService.GetByID(c.Req.Context(), &user.GetUserByIDQuery{ID: userID})
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to get user", err)
}
bitmask := &usr.HelpFlags1
bitmask.AddFlag(user.HelpFlags1(flag))
cmd := user.SetUserHelpFlagCommand{
UserID: c.UserID,
UserID: userID,
HelpFlags1: *bitmask,
}
if err := hs.userService.SetUserHelpFlag(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "Failed to update help flag", err)
return response.Error(http.StatusInternalServerError, "Failed to update help flag", err)
}
return response.JSON(http.StatusOK, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
@ -517,8 +571,13 @@ func (hs *HTTPServer) SetHelpFlag(c *contextmodel.ReqContext) response.Response
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) ClearHelpFlags(c *contextmodel.ReqContext) response.Response {
userID, errResponse := getUserID(c)
if errResponse != nil {
return errResponse
}
cmd := user.SetUserHelpFlagCommand{
UserID: c.UserID,
UserID: userID,
HelpFlags1: user.HelpFlags1(0),
}
@ -529,6 +588,20 @@ func (hs *HTTPServer) ClearHelpFlags(c *contextmodel.ReqContext) response.Respon
return response.JSON(http.StatusOK, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
}
func getUserID(c *contextmodel.ReqContext) (int64, *response.NormalResponse) {
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace != identity.NamespaceUser {
return 0, response.Error(http.StatusForbidden, "Endpoint only available for users", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return 0, response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
}
return userID, nil
}
// swagger:parameters searchUsers
type SearchUsersParams struct {
// Limit the maximum number of users to return per page

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/authn"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/user"
@ -31,7 +32,17 @@ import (
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) GetUserAuthTokens(c *contextmodel.ReqContext) response.Response {
return hs.getUserAuthTokensInternal(c, c.UserID)
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace != identity.NamespaceUser {
return response.Error(http.StatusForbidden, "entity not allowed to revoke tokens", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "failed to parse user id", err)
}
return hs.getUserAuthTokensInternal(c, userID)
}
// swagger:route POST /user/revoke-auth-token signed_in_user revokeUserAuthToken
@ -51,7 +62,18 @@ func (hs *HTTPServer) RevokeUserAuthToken(c *contextmodel.ReqContext) response.R
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.revokeUserAuthTokenInternal(c, c.UserID, cmd)
namespace, identifier := c.SignedInUser.GetNamespacedID()
if namespace != identity.NamespaceUser {
return response.Error(http.StatusForbidden, "entity not allowed to revoke tokens", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "failed to parse user id", err)
}
return hs.revokeUserAuthTokenInternal(c, userID, cmd)
}
func (hs *HTTPServer) RotateUserAuthTokenRedirect(c *contextmodel.ReqContext) response.Response {

@ -4,9 +4,9 @@ import (
"errors"
"time"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/user"
)
var (
@ -50,7 +50,7 @@ type DeleteCommand struct {
type GetApiKeysQuery struct {
OrgID int64
IncludeExpired bool
User *user.SignedInUser
User identity.Requester
}
type GetByNameQuery struct {
KeyName string

@ -33,7 +33,7 @@ type Requester interface {
GetLogin() string
// GetNamespacedID returns the namespace and ID of the active entity.
// The namespace is one of the constants defined in pkg/services/auth/identity.
GetNamespacedID() (string, string)
GetNamespacedID() (namespace string, identifier string)
// GetOrgID returns the ID of the active organization
GetOrgID() int64
// GetOrgRole returns the role of the active entity in the active organization.

Loading…
Cancel
Save