2015-01-19 11:01:04 -06:00
package api
import (
2021-10-04 08:46:09 -05:00
"context"
2020-11-19 06:34:28 -06:00
"errors"
2021-11-29 03:18:01 -06:00
"net/http"
2024-02-16 11:54:59 -06:00
"net/mail"
"net/url"
2022-01-14 10:55:57 -06:00
"strconv"
2022-11-14 06:11:26 -06:00
"strings"
2024-02-16 11:54:59 -06:00
"time"
2020-11-19 06:34:28 -06:00
2017-08-18 07:49:04 -05:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
2023-08-28 03:42:24 -05:00
"github.com/grafana/grafana/pkg/services/auth/identity"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2022-08-10 03:21:33 -05:00
"github.com/grafana/grafana/pkg/services/login"
2022-10-05 08:47:56 -05:00
"github.com/grafana/grafana/pkg/services/org"
2023-01-11 07:20:09 -06:00
"github.com/grafana/grafana/pkg/services/team"
2024-02-16 11:54:59 -06:00
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
2022-07-20 07:50:06 -05:00
"github.com/grafana/grafana/pkg/services/user"
2015-02-19 09:09:49 -06:00
"github.com/grafana/grafana/pkg/util"
2021-11-29 03:18:01 -06:00
"github.com/grafana/grafana/pkg/web"
2015-01-19 11:01:04 -06:00
)
2022-07-27 08:54:37 -05:00
// swagger:route GET /user signed_in_user getSignedInUser
//
// Get (current authenticated user)
//
// Responses:
// 200: userResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetSignedInUser ( c * contextmodel . ReqContext ) response . Response {
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
return hs . getUserUserProfile ( c , userID )
2015-05-18 10:28:15 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /users/{user_id} users getUserByID
//
// Get user by id.
//
// Responses:
// 200: userResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetUserByID ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
return hs . getUserUserProfile ( c , id )
2015-05-18 10:28:15 -05:00
}
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) getUserUserProfile ( c * contextmodel . ReqContext , userID int64 ) response . Response {
2022-09-27 06:58:49 -05:00
query := user . GetUserProfileQuery { UserID : userID }
2015-01-19 11:01:04 -06:00
2022-09-28 06:18:19 -05:00
userProfile , err := hs . userService . GetProfile ( c . Req . Context ( ) , & query )
2022-09-27 06:58:49 -05:00
if err != nil {
2022-07-20 07:50:06 -05:00
if errors . Is ( err , user . ErrUserNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , user . ErrUserNotFound . Error ( ) , nil )
2017-01-30 23:25:55 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to get user" , err )
2015-01-19 11:01:04 -06:00
}
2023-01-27 12:36:54 -06:00
getAuthQuery := login . GetAuthInfoQuery { UserId : userID }
2022-09-27 06:58:49 -05:00
userProfile . AuthLabels = [ ] string { }
2023-03-28 13:32:21 -05:00
if authInfo , err := hs . authInfoService . GetAuthInfo ( c . Req . Context ( ) , & getAuthQuery ) ; err == nil {
authLabel := login . GetAuthProviderLabel ( authInfo . AuthModule )
2022-09-27 06:58:49 -05:00
userProfile . AuthLabels = append ( userProfile . AuthLabels , authLabel )
userProfile . IsExternal = true
2023-12-15 03:58:08 -06:00
oauthInfo := hs . SocialService . GetOAuthInfoProvider ( authInfo . AuthModule )
userProfile . IsExternallySynced = login . IsExternallySynced ( hs . Cfg , authInfo . AuthModule , oauthInfo )
userProfile . IsGrafanaAdminExternallySynced = login . IsGrafanaAdminExternallySynced ( hs . Cfg , oauthInfo , authInfo . AuthModule )
2019-06-25 10:29:07 -05:00
}
2024-03-01 05:08:00 -06:00
userProfile . AccessControl = hs . getAccessControlMetadata ( c , "global.users:id:" , strconv . FormatInt ( userID , 10 ) )
2024-01-23 05:36:22 -06:00
userProfile . AvatarURL = dtos . GetGravatarUrl ( hs . Cfg , userProfile . Email )
2020-01-13 10:10:19 -06:00
2022-09-27 06:58:49 -05:00
return response . JSON ( http . StatusOK , userProfile )
2015-01-19 11:01:04 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /users/lookup users getUserByLoginOrEmail
//
// Get user by login or email.
//
// Responses:
// 200: userResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetUserByLoginOrEmail ( c * contextmodel . ReqContext ) response . Response {
2022-08-04 06:22:43 -05:00
query := user . GetUserByLoginQuery { LoginOrEmail : c . Query ( "loginOrEmail" ) }
usr , err := hs . userService . GetByLogin ( c . Req . Context ( ) , & query )
if err != nil {
2022-07-20 07:50:06 -05:00
if errors . Is ( err , user . ErrUserNotFound ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusNotFound , user . ErrUserNotFound . Error ( ) , nil )
2017-01-30 23:25:55 -06:00
}
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to get user" , err )
2017-01-30 23:25:55 -06:00
}
2023-01-03 08:25:35 -06:00
result := user . UserProfileDTO {
ID : usr . ID ,
2022-08-04 06:22:43 -05:00
Name : usr . Name ,
Email : usr . Email ,
Login : usr . Login ,
Theme : usr . Theme ,
IsGrafanaAdmin : usr . IsAdmin ,
2023-01-03 08:25:35 -06:00
OrgID : usr . OrgID ,
2022-08-04 06:22:43 -05:00
UpdatedAt : usr . Updated ,
CreatedAt : usr . Created ,
2017-01-30 23:25:55 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & result )
2017-01-30 23:25:55 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /user signed_in_user updateSignedInUser
//
// Update signed in User.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
2024-02-16 11:54:59 -06:00
// 409: conflictError
2022-07-27 08:54:37 -05:00
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateSignedInUser ( c * contextmodel . ReqContext ) response . Response {
2022-08-04 07:22:44 -05:00
cmd := user . UpdateUserCommand { }
2022-11-14 06:11:26 -06:00
var err error
if err = web . Bind ( c . Req , & cmd ) ; err != nil {
2021-11-29 03:18:01 -06:00
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-11-14 06:11:26 -06:00
cmd . Email = strings . TrimSpace ( cmd . Email )
cmd . Login = strings . TrimSpace ( cmd . Login )
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
2024-03-01 04:31:06 -06:00
if hs . Cfg . AuthProxy . Enabled {
if hs . Cfg . AuthProxy . HeaderProperty == "email" && cmd . Email != c . SignedInUser . GetEmail ( ) {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusBadRequest , "Not allowed to change email when auth proxy is using email property" , nil )
2016-12-14 15:19:25 -06:00
}
2024-03-01 04:31:06 -06:00
if hs . Cfg . AuthProxy . HeaderProperty == "username" && cmd . Login != c . SignedInUser . GetLogin ( ) {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusBadRequest , "Not allowed to change username when auth proxy is using username property" , nil )
2016-12-14 15:19:25 -06:00
}
}
2023-08-28 03:42:24 -05:00
cmd . UserID = userID
2022-01-26 13:24:05 -06:00
return hs . handleUpdateUser ( c . Req . Context ( ) , cmd )
2015-05-18 12:06:19 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /users/{user_id} users updateUser
//
// Update user.
//
// Update the user identified by id.
//
// Responses:
// 200: okResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
2024-02-16 11:54:59 -06:00
// 409: conflictError
2022-07-27 08:54:37 -05:00
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateUser ( c * contextmodel . ReqContext ) response . Response {
2022-08-04 07:22:44 -05:00
cmd := user . UpdateUserCommand { }
2022-01-14 10:55:57 -06:00
var err error
2022-11-14 06:11:26 -06:00
if err = web . Bind ( c . Req , & cmd ) ; err != nil {
2021-11-29 03:18:01 -06:00
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-11-14 06:11:26 -06:00
cmd . Email = strings . TrimSpace ( cmd . Email )
cmd . Login = strings . TrimSpace ( cmd . Login )
2022-08-04 07:22:44 -05:00
cmd . UserID , err = strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
2022-01-14 10:55:57 -06:00
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2022-11-14 06:11:26 -06:00
2022-01-26 13:24:05 -06:00
return hs . handleUpdateUser ( c . Req . Context ( ) , cmd )
2015-05-18 12:06:19 -05:00
}
2020-09-22 09:22:19 -05:00
// POST /api/users/:id/using/:orgId
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UpdateUserActiveOrg ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
userID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
orgID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":orgId" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "orgId is invalid" , err )
}
2016-05-25 23:51:23 -05:00
2022-01-26 13:24:05 -06:00
if ! hs . validateUsingOrg ( c . Req . Context ( ) , userID , orgID ) {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusUnauthorized , "Not a valid organization" , nil )
2016-05-25 23:51:23 -05:00
}
2022-09-27 06:58:49 -05:00
cmd := user . SetUsingOrgCommand { UserID : userID , OrgID : orgID }
2016-05-25 23:51:23 -05:00
2022-09-27 06:58:49 -05:00
if err := hs . userService . SetUsingOrg ( c . Req . Context ( ) , & cmd ) ; err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to change active organization" , err )
2016-05-25 23:51:23 -05:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Active organization changed" )
2016-05-25 23:51:23 -05:00
}
2022-08-04 07:22:44 -05:00
func ( hs * HTTPServer ) handleUpdateUser ( ctx context . Context , cmd user . UpdateUserCommand ) response . Response {
2022-11-14 06:11:26 -06:00
// external user -> user data cannot be updated
isExternal , err := hs . isExternalUser ( ctx , cmd . UserID )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to validate User" , err )
}
if isExternal {
return response . Error ( http . StatusForbidden , "User info cannot be updated for external Users" , nil )
}
2023-10-30 04:44:26 -05:00
if len ( cmd . Login ) == 0 {
cmd . Login = cmd . Email
}
// if login is still empty both email and login field is missing
if len ( cmd . Login ) == 0 {
return response . Err ( user . ErrEmptyUsernameAndEmail . Errorf ( "user cannot be created with empty username and email" ) )
}
2024-02-16 11:54:59 -06:00
// If email is being updated, we need to verify it. Likewise, if username is being updated and the new username
// is an email, we also need to verify it.
// To avoid breaking changes, email verification is implemented in a way that if the email field is being updated,
// all the other fields being updated in the same request are disregarded. We do this because email might need to
// be verified and if so, it goes through a different code flow.
if hs . Cfg . Smtp . Enabled && hs . Cfg . VerifyEmailEnabled {
query := user . GetUserByIDQuery { ID : cmd . UserID }
usr , err := hs . userService . GetByID ( ctx , & query )
if err != nil {
if errors . Is ( err , user . ErrUserNotFound ) {
2024-03-14 07:25:28 -05:00
return response . Error ( http . StatusNotFound , user . ErrUserNotFound . Error ( ) , err )
2024-02-16 11:54:59 -06:00
}
return response . Error ( http . StatusInternalServerError , "Failed to get user" , err )
}
if len ( cmd . Email ) != 0 && usr . Email != cmd . Email {
2024-03-14 07:25:28 -05:00
normalized , err := ValidateAndNormalizeEmail ( cmd . Email )
2024-02-16 11:54:59 -06:00
if err != nil {
return response . Error ( http . StatusBadRequest , "Invalid email address" , err )
}
2024-03-14 07:25:28 -05:00
return hs . verifyEmailUpdate ( ctx , normalized , user . EmailUpdateAction , usr )
2024-02-16 11:54:59 -06:00
}
if len ( cmd . Login ) != 0 && usr . Login != cmd . Login {
2024-03-14 07:25:28 -05:00
normalized , err := ValidateAndNormalizeEmail ( cmd . Login )
if err == nil && usr . Email != normalized {
return hs . verifyEmailUpdate ( ctx , cmd . Login , user . LoginUpdateAction , usr )
2024-02-16 11:54:59 -06:00
}
}
}
2022-08-04 07:22:44 -05:00
if err := hs . userService . Update ( ctx , & cmd ) ; err != nil {
2022-07-20 07:50:06 -05:00
if errors . Is ( err , user . ErrCaseInsensitive ) {
2022-06-24 09:59:45 -05:00
return response . Error ( http . StatusConflict , "Update would result in user login conflict" , err )
}
2023-10-11 07:27:43 -05:00
return response . ErrOrFallback ( http . StatusInternalServerError , "Failed to update user" , err )
2015-01-19 11:01:04 -06:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "User updated" )
2015-01-19 11:01:04 -06:00
}
2024-02-16 11:54:59 -06:00
func ( hs * HTTPServer ) verifyEmailUpdate ( ctx context . Context , email string , field user . UpdateEmailActionType , usr * user . User ) response . Response {
2024-03-14 07:25:28 -05:00
if err := hs . userVerifier . VerifyEmail ( ctx , user . VerifyEmailCommand {
User : * usr ,
2024-02-16 11:54:59 -06:00
Email : email ,
2024-03-14 07:25:28 -05:00
Action : field ,
} ) ; err != nil {
return response . ErrOrFallback ( http . StatusInternalServerError , "Failed to generate email verification" , err )
2024-02-16 11:54:59 -06:00
}
return response . Success ( "Email sent for verification" )
}
// swagger:route GET /user/email/update user updateUserEmail
//
// Update user email.
//
// Update the email of user given a verification code.
//
// Responses:
// 302: okResponse
func ( hs * HTTPServer ) UpdateUserEmail ( c * contextmodel . ReqContext ) response . Response {
var err error
q := c . Req . URL . Query ( )
code , err := url . QueryUnescape ( q . Get ( "code" ) )
if err != nil || code == "" {
return hs . RedirectResponseWithError ( c , errors . New ( "bad request data" ) )
}
tempUser , err := hs . validateEmailCode ( c . Req . Context ( ) , code )
if err != nil {
return hs . RedirectResponseWithError ( c , err )
}
cmd , err := hs . updateCmdFromEmailVerification ( c . Req . Context ( ) , tempUser )
if err != nil {
return hs . RedirectResponseWithError ( c , err )
}
if err := hs . userService . Update ( c . Req . Context ( ) , cmd ) ; err != nil {
if errors . Is ( err , user . ErrCaseInsensitive ) {
return hs . RedirectResponseWithError ( c , errors . New ( "update would result in user login conflict" ) )
}
return hs . RedirectResponseWithError ( c , errors . New ( "failed to update user" ) )
}
// Mark temp user as completed
updateTmpUserCmd := tempuser . UpdateTempUserStatusCommand { Code : code , Status : tempuser . TmpUserEmailUpdateCompleted }
if err := hs . tempUserService . UpdateTempUserStatus ( c . Req . Context ( ) , & updateTmpUserCmd ) ; err != nil {
return hs . RedirectResponseWithError ( c , errors . New ( "failed to update verification status" ) )
}
return response . Redirect ( hs . Cfg . AppSubURL + "/profile" )
}
2022-11-14 06:11:26 -06:00
func ( hs * HTTPServer ) isExternalUser ( ctx context . Context , userID int64 ) ( bool , error ) {
2023-01-27 12:36:54 -06:00
getAuthQuery := login . GetAuthInfoQuery { UserId : userID }
2022-11-14 06:11:26 -06:00
var err error
2023-03-28 13:32:21 -05:00
if _ , err = hs . authInfoService . GetAuthInfo ( ctx , & getAuthQuery ) ; err == nil {
2022-11-14 06:11:26 -06:00
return true , nil
}
if errors . Is ( err , user . ErrUserNotFound ) {
return false , nil
}
return false , err
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /user/orgs signed_in_user getSignedInUserOrgList
//
// Organizations of the actual User.
//
// Return a list of all organizations of the current user.
//
// Security:
// - basic:
//
// Responses:
// 200: getSignedInUserOrgListResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetSignedInUserOrgList ( c * contextmodel . ReqContext ) response . Response {
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
return hs . getUserOrgList ( c . Req . Context ( ) , userID )
2015-05-18 10:28:15 -05:00
}
2015-01-19 11:01:04 -06:00
2022-07-27 08:54:37 -05:00
// swagger:route GET /user/teams signed_in_user getSignedInUserTeamList
//
// Teams that the actual User is member of.
//
// Return a list of all teams that the current user is member of.
//
// Responses:
// 200: getSignedInUserTeamListResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetSignedInUserTeamList ( c * contextmodel . ReqContext ) response . Response {
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
return hs . getUserTeamList ( c , c . SignedInUser . GetOrgID ( ) , userID )
2018-11-19 03:08:10 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /users/{user_id}/teams users getUserTeams
//
// Get teams for user.
//
// Get teams for user identified by id.
//
// Responses:
// 200: getUserTeamsResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetUserTeams ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2023-08-28 03:42:24 -05:00
return hs . getUserTeamList ( c , c . SignedInUser . GetOrgID ( ) , id )
2018-11-19 03:08:10 -06:00
}
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) getUserTeamList ( c * contextmodel . ReqContext , orgID int64 , userID int64 ) response . Response {
2023-01-11 07:20:09 -06:00
query := team . GetTeamsByUserQuery { OrgID : orgID , UserID : userID , SignedInUser : c . SignedInUser }
2018-08-08 03:26:05 -05:00
2023-01-11 07:20:09 -06:00
queryResult , err := hs . teamService . GetTeamsByUser ( c . Req . Context ( ) , & query )
if err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to get user teams" , err )
2018-08-08 03:26:05 -05:00
}
2023-01-11 07:20:09 -06:00
for _ , team := range queryResult {
2024-01-23 05:36:22 -06:00
team . AvatarURL = dtos . GetGravatarUrlWithDefault ( hs . Cfg , team . Email , team . Name )
2018-08-08 03:26:05 -05:00
}
2023-01-11 07:20:09 -06:00
return response . JSON ( http . StatusOK , queryResult )
2018-08-08 03:26:05 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /users/{user_id}/orgs users getUserOrgList
//
// Get organizations for user.
//
// Get organizations for user identified by id.
//
// Responses:
// 200: getUserOrgListResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) GetUserOrgList ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
id , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2022-01-26 13:24:05 -06:00
return hs . getUserOrgList ( c . Req . Context ( ) , id )
2015-05-18 10:28:15 -05:00
}
2015-01-19 11:01:04 -06:00
2022-01-26 13:24:05 -06:00
func ( hs * HTTPServer ) getUserOrgList ( ctx context . Context , userID int64 ) response . Response {
2022-10-05 08:47:56 -05:00
query := org . GetUserOrgListQuery { UserID : userID }
2015-05-18 10:28:15 -05:00
2022-10-05 08:47:56 -05:00
result , err := hs . orgService . GetUserOrgList ( ctx , & query )
if err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to get user organizations" , err )
2015-01-19 11:01:04 -06:00
}
2022-10-05 08:47:56 -05:00
return response . JSON ( http . StatusOK , result )
2015-01-19 11:01:04 -06:00
}
2022-01-26 13:24:05 -06:00
func ( hs * HTTPServer ) validateUsingOrg ( ctx context . Context , userID int64 , orgID int64 ) bool {
2022-10-05 08:47:56 -05:00
query := org . GetUserOrgListQuery { UserID : userID }
2015-01-19 11:01:04 -06:00
2022-10-05 08:47:56 -05:00
result , err := hs . orgService . GetUserOrgList ( ctx , & query )
if err != nil {
2015-01-19 11:01:04 -06:00
return false
}
2015-02-23 13:07:49 -06:00
// validate that the org id in the list
2015-01-19 11:01:04 -06:00
valid := false
2022-10-05 08:47:56 -05:00
for _ , other := range result {
if other . OrgID == orgID {
2015-01-19 11:01:04 -06:00
valid = true
}
}
return valid
}
2022-07-27 08:54:37 -05:00
// swagger:route POST /user/using/{org_id} signed_in_user userSetUsingOrg
//
// Switch user context for signed in user.
//
// Switch user context to the given organization.
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) UserSetUsingOrg ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
orgID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2015-01-19 11:01:04 -06:00
2023-08-28 03:42:24 -05:00
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 )
2015-01-19 11:01:04 -06:00
}
2023-08-28 03:42:24 -05:00
cmd := user . SetUsingOrgCommand { UserID : userID , OrgID : orgID }
2015-01-19 11:01:04 -06:00
2022-09-27 06:58:49 -05:00
if err := hs . userService . SetUsingOrg ( c . Req . Context ( ) , & cmd ) ; err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to change active organization" , err )
2015-01-19 11:01:04 -06:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "Active organization changed" )
2015-01-19 11:01:04 -06:00
}
2015-02-19 09:09:49 -06:00
2016-04-09 12:27:06 -05:00
// GET /profile/switch-org/:id
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) ChangeActiveOrgAndRedirectToHome ( c * contextmodel . ReqContext ) {
2022-01-14 10:55:57 -06:00
orgID , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
c . JsonApiErr ( http . StatusBadRequest , "id is invalid" , err )
return
}
2016-04-09 12:27:06 -05:00
2023-08-28 03:42:24 -05:00
namespace , identifier := c . SignedInUser . GetNamespacedID ( )
if namespace != identity . NamespaceUser {
c . JsonApiErr ( http . StatusForbidden , "Endpoint only available for users" , nil )
return
2016-04-09 12:27:06 -05:00
}
2023-08-28 03:42:24 -05:00
userID , err := identity . IntIdentifier ( namespace , identifier )
if err != nil {
c . JsonApiErr ( http . StatusInternalServerError , "Failed to parse user id" , err )
return
}
2016-04-09 12:27:06 -05:00
2023-08-28 03:42:24 -05:00
if ! hs . validateUsingOrg ( c . Req . Context ( ) , userID , orgID ) {
hs . NotFoundHandler ( c )
return
}
cmd := user . SetUsingOrgCommand { UserID : userID , OrgID : orgID }
2022-09-27 06:58:49 -05:00
if err := hs . userService . SetUsingOrg ( c . Req . Context ( ) , & cmd ) ; err != nil {
2018-10-12 04:26:42 -05:00
hs . NotFoundHandler ( c )
2023-08-28 03:42:24 -05:00
return
2016-04-09 12:27:06 -05:00
}
2021-03-10 05:41:29 -06:00
c . Redirect ( hs . Cfg . AppSubURL + "/" )
2016-04-09 12:27:06 -05:00
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /user/password signed_in_user changeUserPassword
//
// Change Password.
//
// Changes the password for the user.
//
// Security:
// - basic:
//
// Responses:
// 200: okResponse
// 400: badRequestError
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) ChangeUserPassword ( c * contextmodel . ReqContext ) response . Response {
2022-08-04 08:05:05 -05:00
cmd := user . ChangeUserPasswordCommand { }
2021-11-29 03:18:01 -06:00
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2016-12-14 15:19:25 -06:00
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
userQuery := user . GetUserByIDQuery { ID : userID }
2015-02-19 09:09:49 -06:00
2023-01-31 04:04:55 -06:00
usr , err := hs . userService . GetByID ( c . Req . Context ( ) , & userQuery )
2022-08-02 09:58:05 -05:00
if err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Could not read user from database" , err )
2015-02-19 09:09:49 -06:00
}
2023-01-31 04:04:55 -06:00
getAuthQuery := login . GetAuthInfoQuery { UserId : usr . ID }
2023-03-28 13:32:21 -05:00
if authInfo , err := hs . authInfoService . GetAuthInfo ( c . Req . Context ( ) , & getAuthQuery ) ; err == nil {
2023-12-29 07:23:05 -06:00
oauthInfo := hs . SocialService . GetOAuthInfoProvider ( authInfo . AuthModule )
if login . IsProviderEnabled ( hs . Cfg , authInfo . AuthModule , oauthInfo ) {
return response . Error ( http . StatusBadRequest , "Cannot update external user password" , err )
2022-08-08 00:12:39 -05:00
}
}
2024-02-16 04:58:05 -06:00
passwordHashed , err := util . EncodePassword ( string ( cmd . OldPassword ) , usr . Salt )
2019-10-23 03:40:12 -05:00
if err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to encode password" , err )
2019-10-23 03:40:12 -05:00
}
2024-02-16 04:58:05 -06:00
if user . Password ( passwordHashed ) != usr . Password {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusUnauthorized , "Invalid old password" , nil )
2015-02-19 09:09:49 -06:00
}
2024-02-16 04:58:05 -06:00
if err := cmd . NewPassword . Validate ( hs . Cfg ) ; err != nil {
c . Logger . Warn ( "the new password doesn't meet the password policy criteria" , "err" , err )
return response . Err ( err )
2015-02-19 09:09:49 -06:00
}
2023-08-28 03:42:24 -05:00
cmd . UserID = userID
2024-02-16 04:58:05 -06:00
encodedPassword , err := util . EncodePassword ( string ( cmd . NewPassword ) , usr . Salt )
2019-10-23 03:40:12 -05:00
if err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to encode password" , err )
2019-10-23 03:40:12 -05:00
}
2024-02-16 04:58:05 -06:00
cmd . NewPassword = user . Password ( encodedPassword )
2015-02-19 09:09:49 -06:00
2022-08-04 08:05:05 -05:00
if err := hs . userService . ChangePassword ( c . Req . Context ( ) , & cmd ) ; err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to change user password" , err )
2015-02-19 09:09:49 -06:00
}
2021-01-15 07:43:20 -06:00
return response . Success ( "User password changed" )
2015-02-19 09:09:49 -06:00
}
2015-05-19 04:47:14 -05:00
2020-11-10 16:36:35 -06:00
// redirectToChangePassword handles GET /.well-known/change-password.
2023-01-27 01:50:36 -06:00
func redirectToChangePassword ( c * contextmodel . ReqContext ) {
2020-11-10 16:36:35 -06:00
c . Redirect ( "/profile/password" , 302 )
}
2022-07-27 08:54:37 -05:00
// swagger:route PUT /user/helpflags/{flag_id} signed_in_user setHelpFlag
//
// Set user help flag.
//
// Responses:
// 200: helpFlagResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) SetHelpFlag ( c * contextmodel . ReqContext ) response . Response {
2022-01-14 10:55:57 -06:00
flag , err := strconv . ParseInt ( web . Params ( c . Req ) [ ":id" ] , 10 , 64 )
if err != nil {
return response . Error ( http . StatusBadRequest , "id is invalid" , err )
}
2016-11-09 03:41:39 -06:00
2023-08-28 03:42:24 -05:00
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
2022-08-10 04:56:48 -05:00
bitmask . AddFlag ( user . HelpFlags1 ( flag ) )
2016-11-09 03:41:39 -06:00
2022-09-27 06:58:49 -05:00
cmd := user . SetUserHelpFlagCommand {
2023-08-28 03:42:24 -05:00
UserID : userID ,
2016-11-09 03:41:39 -06:00
HelpFlags1 : * bitmask ,
}
2022-09-27 06:58:49 -05:00
if err := hs . userService . SetUserHelpFlag ( c . Req . Context ( ) , & cmd ) ; err != nil {
2023-08-28 03:42:24 -05:00
return response . Error ( http . StatusInternalServerError , "Failed to update help flag" , err )
2016-11-09 03:41:39 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & util . DynMap { "message" : "Help flag set" , "helpFlags1" : cmd . HelpFlags1 } )
2016-11-09 03:41:39 -06:00
}
2022-07-27 08:54:37 -05:00
// swagger:route GET /user/helpflags/clear signed_in_user clearHelpFlags
//
// Clear user help flag.
//
// Responses:
// 200: helpFlagResponse
// 401: unauthorisedError
// 403: forbiddenError
// 500: internalServerError
2023-01-27 01:50:36 -06:00
func ( hs * HTTPServer ) ClearHelpFlags ( c * contextmodel . ReqContext ) response . Response {
2023-08-28 03:42:24 -05:00
userID , errResponse := getUserID ( c )
if errResponse != nil {
return errResponse
}
2022-09-27 06:58:49 -05:00
cmd := user . SetUserHelpFlagCommand {
2023-08-28 03:42:24 -05:00
UserID : userID ,
2022-08-10 04:56:48 -05:00
HelpFlags1 : user . HelpFlags1 ( 0 ) ,
2016-11-09 03:41:39 -06:00
}
2022-09-27 06:58:49 -05:00
if err := hs . userService . SetUserHelpFlag ( c . Req . Context ( ) , & cmd ) ; err != nil {
2024-02-27 10:39:51 -06:00
return response . Error ( http . StatusInternalServerError , "Failed to update help flag" , err )
2016-11-09 03:41:39 -06:00
}
2022-04-15 07:01:58 -05:00
return response . JSON ( http . StatusOK , & util . DynMap { "message" : "Help flag set" , "helpFlags1" : cmd . HelpFlags1 } )
2016-11-09 03:41:39 -06:00
}
2019-07-10 04:06:51 -05:00
2023-08-28 03:42:24 -05:00
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
}
2024-02-16 11:54:59 -06:00
func ( hs * HTTPServer ) updateCmdFromEmailVerification ( ctx context . Context , tempUser * tempuser . TempUserDTO ) ( * user . UpdateUserCommand , error ) {
userQuery := user . GetUserByLoginQuery { LoginOrEmail : tempUser . InvitedByLogin }
usr , err := hs . userService . GetByLogin ( ctx , & userQuery )
if err != nil {
if errors . Is ( err , user . ErrUserNotFound ) {
return nil , user . ErrUserNotFound
}
return nil , errors . New ( "failed to get user" )
}
cmd := & user . UpdateUserCommand { UserID : usr . ID , Email : tempUser . Email }
switch tempUser . Name {
case string ( user . EmailUpdateAction ) :
// User updated the email field
if _ , err := mail . ParseAddress ( usr . Login ) ; err == nil {
// If username was also an email, we update it to keep it in sync with the email field
cmd . Login = tempUser . Email
}
case string ( user . LoginUpdateAction ) :
// User updated the username field with a new email
cmd . Login = tempUser . Email
default :
return nil , errors . New ( "trying to update email on unknown field" )
}
return cmd , nil
}
func ( hs * HTTPServer ) validateEmailCode ( ctx context . Context , code string ) ( * tempuser . TempUserDTO , error ) {
tempUserQuery := tempuser . GetTempUserByCodeQuery { Code : code }
tempUser , err := hs . tempUserService . GetTempUserByCode ( ctx , & tempUserQuery )
if err != nil {
if errors . Is ( err , tempuser . ErrTempUserNotFound ) {
return nil , errors . New ( "invalid email verification code" )
}
return nil , errors . New ( "failed to read temp user" )
}
if tempUser . Status != tempuser . TmpUserEmailUpdateStarted {
return nil , errors . New ( "invalid email verification code" )
}
if ! tempUser . EmailSent {
return nil , errors . New ( "verification email was not recorded as sent" )
}
if tempUser . EmailSentOn . Add ( hs . Cfg . VerificationEmailMaxLifetime ) . Before ( time . Now ( ) ) {
return nil , errors . New ( "invalid email verification code" )
}
return tempUser , nil
}
2022-07-27 08:54:37 -05:00
// swagger:parameters searchUsers
type SearchUsersParams struct {
// Limit the maximum number of users to return per page
// in:query
// required:false
// default:1000
Limit int64 ` json:"perpage" `
// Page index for starting fetching users
// in:query
// required:false
// default:1
Page int64 ` json:"page" `
}
// swagger:parameters searchUsersWithPaging
type SearchUsersWithPagingParams struct {
// Limit the maximum number of users to return per page
// in:query
// required:false
// default:1000
Limit int64 ` json:"perpage" `
// Page index for starting fetching users
// in:query
// required:false
// default:1
Page int64 ` json:"page" `
// Query allows return results where the query value is contained in one of the name, login or email fields. Query values with spaces need to be URL encoded e.g. query=Jane%20Doe
// in:query
// required:false
Query string ` json:"query" `
}
// swagger:parameters updateSignedInUser
type UpdateSignedInUserParams struct {
// To change the email, name, login, theme, provide another one.
// in:body
// required:true
2023-01-03 08:25:35 -06:00
Body user . UpdateUserCommand ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:parameters userSetUsingOrg
type UserSetUsingOrgParams struct {
// in:path
// required:true
OrgID int64 ` json:"org_id" `
}
// swagger:parameters setHelpFlag
type SetHelpFlagParams struct {
// in:path
// required:true
FlagID string ` json:"flag_id" `
}
// swagger:parameters changeUserPassword
type ChangeUserPasswordParams struct {
// To change the email, name, login, theme, provide another one.
// in:body
// required:true
2023-01-03 08:25:35 -06:00
Body user . ChangeUserPasswordCommand ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:parameters getUserByID
type GetUserByIDParams struct {
// in:path
// required:true
UserID int64 ` json:"user_id" `
}
// swagger:parameters getUserOrgList
type GetUserOrgListParams struct {
// in:path
// required:true
UserID int64 ` json:"user_id" `
}
// swagger:parameters getUserTeams
type GetUserTeamsParams struct {
// in:path
// required:true
UserID int64 ` json:"user_id" `
}
// swagger:parameters getUserByLoginOrEmail
type GetUserByLoginOrEmailParams struct {
// loginOrEmail of the user
// in:query
// required:true
LoginOrEmail string ` json:"loginOrEmail" `
}
// swagger:parameters updateUser
type UpdateUserParams struct {
// To change the email, name, login, theme, provide another one.
// in:body
// required:true
2023-01-03 08:25:35 -06:00
Body user . UpdateUserCommand ` json:"body" `
2022-07-27 08:54:37 -05:00
// in:path
// required:true
UserID int64 ` json:"user_id" `
}
// swagger:response userResponse
type UserResponse struct {
// The response message
// in: body
2023-01-03 08:25:35 -06:00
Body user . UserProfileDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response getUserOrgListResponse
type GetUserOrgListResponse struct {
// The response message
// in: body
2023-01-04 09:20:26 -06:00
Body [ ] * org . UserOrgDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response getSignedInUserOrgListResponse
type GetSignedInUserOrgListResponse struct {
// The response message
// in: body
2023-01-04 09:20:26 -06:00
Body [ ] * org . UserOrgDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response getUserTeamsResponse
type GetUserTeamsResponse struct {
// The response message
// in: body
2023-01-11 07:20:09 -06:00
Body [ ] * team . TeamDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response getSignedInUserTeamListResponse
type GetSignedInUserTeamListResponse struct {
// The response message
// in: body
2023-01-11 07:20:09 -06:00
Body [ ] * team . TeamDTO ` json:"body" `
2022-07-27 08:54:37 -05:00
}
// swagger:response helpFlagResponse
type HelpFlagResponse struct {
// The response message
// in: body
Body struct {
HelpFlags1 int64 ` json:"helpFlags1" `
Message string ` json:"message" `
} ` json:"body" `
}