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 }