github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/api/user.go (about)

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net/http"
     7  	"time"
     8  
     9  	"github.com/pyroscope-io/pyroscope/pkg/model"
    10  	"github.com/pyroscope-io/pyroscope/pkg/server/httputils"
    11  )
    12  
    13  //go:generate mockgen -destination mocks/user.go -package mocks . UserService
    14  
    15  type UserService interface {
    16  	CreateUser(context.Context, model.CreateUserParams) (model.User, error)
    17  	FindUserByID(context.Context, uint) (model.User, error)
    18  	GetAllUsers(context.Context) ([]model.User, error)
    19  	UpdateUserByID(context.Context, uint, model.UpdateUserParams) (model.User, error)
    20  	UpdateUserPasswordByID(context.Context, uint, model.UpdateUserPasswordParams) error
    21  	DeleteUserByID(context.Context, uint) error
    22  }
    23  
    24  type UserHandler struct {
    25  	userService UserService
    26  	httpUtils   httputils.Utils
    27  }
    28  
    29  func NewUserHandler(userService UserService, httpUtils httputils.Utils) UserHandler {
    30  	return UserHandler{
    31  		userService: userService,
    32  		httpUtils:   httpUtils,
    33  	}
    34  }
    35  
    36  type user struct {
    37  	ID                uint       `json:"id"`
    38  	Name              string     `json:"name"`
    39  	Email             *string    `json:"email,omitempty"`
    40  	FullName          *string    `json:"fullName,omitempty"`
    41  	Role              model.Role `json:"role"`
    42  	IsDisabled        bool       `json:"isDisabled"`
    43  	IsExternal        bool       `json:"isExternal"`
    44  	CreatedAt         time.Time  `json:"createdAt"`
    45  	UpdatedAt         time.Time  `json:"updatedAt"`
    46  	LastSeenAt        *time.Time `json:"lastSeenAt,omitempty"`
    47  	PasswordChangedAt time.Time  `json:"passwordChangedAt"`
    48  }
    49  
    50  type createUserRequest struct {
    51  	Name     string     `json:"name"`
    52  	Email    *string    `json:"email,omitempty"`
    53  	FullName *string    `json:"fullName,omitempty"`
    54  	Password []byte     `json:"password"`
    55  	Role     model.Role `json:"role"`
    56  }
    57  
    58  type updateUserRequest struct {
    59  	Name     *string `json:"name,omitempty"`
    60  	Email    *string `json:"email,omitempty"`
    61  	FullName *string `json:"fullName,omitempty"`
    62  }
    63  
    64  type resetUserPasswordRequest struct {
    65  	Password []byte `json:"password"`
    66  }
    67  
    68  type changeUserPasswordRequest struct {
    69  	OldPassword []byte `json:"oldPassword"`
    70  	NewPassword []byte `json:"newPassword"`
    71  }
    72  
    73  type changeUserRoleRequest struct {
    74  	Role model.Role `json:"role"`
    75  }
    76  
    77  func userFromModel(u model.User) user {
    78  	return user{
    79  		ID:                u.ID,
    80  		Name:              u.Name,
    81  		Email:             u.Email,
    82  		FullName:          u.FullName,
    83  		Role:              u.Role,
    84  		IsDisabled:        model.IsUserDisabled(u),
    85  		IsExternal:        model.IsUserExternal(u),
    86  		PasswordChangedAt: u.PasswordChangedAt,
    87  		LastSeenAt:        u.LastSeenAt,
    88  		CreatedAt:         u.CreatedAt,
    89  		UpdatedAt:         u.UpdatedAt,
    90  	}
    91  }
    92  
    93  func (h UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    94  	var req createUserRequest
    95  	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
    96  		h.httpUtils.HandleError(r, w, httputils.JSONError{Err: err})
    97  		return
    98  	}
    99  	params := model.CreateUserParams{
   100  		Name:     req.Name,
   101  		Email:    req.Email,
   102  		FullName: req.FullName,
   103  		Password: string(req.Password),
   104  		Role:     req.Role,
   105  	}
   106  	u, err := h.userService.CreateUser(r.Context(), params)
   107  	if err != nil {
   108  		h.httpUtils.HandleError(r, w, err)
   109  		return
   110  	}
   111  	w.WriteHeader(http.StatusCreated)
   112  	h.httpUtils.MustJSON(r, w, userFromModel(u))
   113  }
   114  
   115  func (h UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
   116  	id, err := h.httpUtils.IDFromRequest(r)
   117  	if err != nil {
   118  		h.httpUtils.HandleError(r, w, err)
   119  		return
   120  	}
   121  	u, err := h.userService.FindUserByID(r.Context(), id)
   122  	if err != nil {
   123  		h.httpUtils.HandleError(r, w, err)
   124  		return
   125  	}
   126  	h.httpUtils.MustJSON(r, w, userFromModel(u))
   127  }
   128  
   129  func (h UserHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
   130  	u, err := h.userService.GetAllUsers(r.Context())
   131  	if err != nil {
   132  		h.httpUtils.HandleError(r, w, err)
   133  		return
   134  	}
   135  	users := make([]user, len(u))
   136  	for i := range u {
   137  		users[i] = userFromModel(u[i])
   138  	}
   139  	h.httpUtils.MustJSON(r, w, users)
   140  }
   141  
   142  func (h UserHandler) UpdateUser(w http.ResponseWriter, r *http.Request) {
   143  	id, err := h.httpUtils.IDFromRequest(r)
   144  	if err != nil {
   145  		h.httpUtils.HandleError(r, w, err)
   146  		return
   147  	}
   148  	h.updateUser(w, r, id)
   149  }
   150  
   151  func (h UserHandler) updateUser(w http.ResponseWriter, r *http.Request, id uint) {
   152  	var req updateUserRequest
   153  	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
   154  		h.httpUtils.HandleError(r, w, httputils.JSONError{Err: err})
   155  		return
   156  	}
   157  	params := model.UpdateUserParams{
   158  		Name:     req.Name,
   159  		Email:    req.Email,
   160  		FullName: req.FullName,
   161  	}
   162  	u, err := h.userService.UpdateUserByID(r.Context(), id, params)
   163  	if err != nil {
   164  		h.httpUtils.HandleError(r, w, err)
   165  		return
   166  	}
   167  	h.httpUtils.MustJSON(r, w, userFromModel(u))
   168  }
   169  
   170  func (h UserHandler) ChangeUserPassword(w http.ResponseWriter, r *http.Request) {
   171  	id, err := h.httpUtils.IDFromRequest(r)
   172  	if err != nil {
   173  		h.httpUtils.HandleError(r, w, err)
   174  		return
   175  	}
   176  	if isSameUser(r.Context(), id) {
   177  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   178  		return
   179  	}
   180  	var req resetUserPasswordRequest
   181  	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
   182  		h.httpUtils.HandleError(r, w, httputils.JSONError{Err: err})
   183  		return
   184  	}
   185  	params := model.UpdateUserParams{Password: model.String(string(req.Password))}
   186  	if _, err = h.userService.UpdateUserByID(r.Context(), id, params); err != nil {
   187  		h.httpUtils.HandleError(r, w, err)
   188  		return
   189  	}
   190  	w.WriteHeader(http.StatusNoContent)
   191  }
   192  
   193  func (h UserHandler) ChangeUserRole(w http.ResponseWriter, r *http.Request) {
   194  	id, err := h.httpUtils.IDFromRequest(r)
   195  	if err != nil {
   196  		h.httpUtils.HandleError(r, w, err)
   197  		return
   198  	}
   199  	if isSameUser(r.Context(), id) {
   200  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   201  		return
   202  	}
   203  	var req changeUserRoleRequest
   204  	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
   205  		h.httpUtils.HandleError(r, w, httputils.JSONError{Err: err})
   206  		return
   207  	}
   208  	params := model.UpdateUserParams{Role: &req.Role}
   209  	if _, err = h.userService.UpdateUserByID(r.Context(), id, params); err != nil {
   210  		h.httpUtils.HandleError(r, w, err)
   211  		return
   212  	}
   213  	w.WriteHeader(http.StatusNoContent)
   214  }
   215  
   216  func (h UserHandler) DisableUser(w http.ResponseWriter, r *http.Request) {
   217  	h.setUserDisabled(w, r, true)
   218  }
   219  
   220  func (h UserHandler) EnableUser(w http.ResponseWriter, r *http.Request) {
   221  	h.setUserDisabled(w, r, false)
   222  }
   223  
   224  func (h UserHandler) setUserDisabled(w http.ResponseWriter, r *http.Request, disabled bool) {
   225  	id, err := h.httpUtils.IDFromRequest(r)
   226  	if err != nil {
   227  		h.httpUtils.HandleError(r, w, err)
   228  		return
   229  	}
   230  	if isSameUser(r.Context(), id) {
   231  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   232  		return
   233  	}
   234  	params := model.UpdateUserParams{IsDisabled: &disabled}
   235  	if _, err = h.userService.UpdateUserByID(r.Context(), id, params); err != nil {
   236  		h.httpUtils.HandleError(r, w, err)
   237  		return
   238  	}
   239  	w.WriteHeader(http.StatusNoContent)
   240  }
   241  
   242  func (h UserHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
   243  	id, err := h.httpUtils.IDFromRequest(r)
   244  	if err != nil {
   245  		h.httpUtils.HandleError(r, w, err)
   246  		return
   247  	}
   248  	if err = h.userService.DeleteUserByID(r.Context(), id); err != nil {
   249  		h.httpUtils.HandleError(r, w, err)
   250  		return
   251  	}
   252  	w.WriteHeader(http.StatusNoContent)
   253  }
   254  
   255  func (h UserHandler) GetAuthenticatedUser(w http.ResponseWriter, r *http.Request) {
   256  	u, ok := model.UserFromContext(r.Context())
   257  	if !ok {
   258  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   259  		return
   260  	}
   261  	h.httpUtils.MustJSON(r, w, userFromModel(u))
   262  }
   263  
   264  func (h UserHandler) UpdateAuthenticatedUser(w http.ResponseWriter, r *http.Request) {
   265  	u, ok := model.UserFromContext(r.Context())
   266  	if !ok {
   267  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   268  		return
   269  	}
   270  	h.updateUser(w, r, u.ID)
   271  }
   272  
   273  func (h UserHandler) ChangeAuthenticatedUserPassword(w http.ResponseWriter, r *http.Request) {
   274  	u, ok := model.UserFromContext(r.Context())
   275  	if !ok {
   276  		h.httpUtils.HandleError(r, w, model.ErrPermissionDenied)
   277  		return
   278  	}
   279  	var req changeUserPasswordRequest
   280  	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
   281  		h.httpUtils.HandleError(r, w, httputils.JSONError{Err: err})
   282  		return
   283  	}
   284  	params := model.UpdateUserPasswordParams{
   285  		OldPassword: string(req.OldPassword),
   286  		NewPassword: string(req.NewPassword),
   287  	}
   288  	if err := h.userService.UpdateUserPasswordByID(r.Context(), u.ID, params); err != nil {
   289  		h.httpUtils.HandleError(r, w, err)
   290  		return
   291  	}
   292  	w.WriteHeader(http.StatusNoContent)
   293  }
   294  
   295  func isSameUser(ctx context.Context, id uint) bool {
   296  	if u, ok := model.UserFromContext(ctx); ok {
   297  		return id == u.ID
   298  	}
   299  	return false
   300  }